pipwave-ekyc-uikit 3.8.2-beta.1 → 3.8.2-beta.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pipwave-ekyc-uikit",
3
- "version": "3.8.2-beta.1",
3
+ "version": "3.8.2-beta.2",
4
4
  "description": "",
5
5
  "main": "dist/pw-bundle.js",
6
6
  "module": "dist/pw-bundle.mjs",
@@ -1,47 +0,0 @@
1
- ---
2
- description: Write a release message for the current branch's changes in our house style
3
- argument-hint: <Fix|Enhancement|New> [target-branch] [--short]
4
- ---
5
-
6
- Write a release message for the changes on the CURRENT branch, which are merging
7
- into a target branch. Do not repeat prompting overhead back to me — just produce
8
- the message.
9
-
10
- ## Inputs (from "$ARGUMENTS")
11
- - **Type** (required): one of `Fix`, `Enhancement`, `New` (case-insensitive; also accept `Fixes`).
12
- - **Target branch** (optional): the branch these changes merge INTO. Default `origin/master`.
13
- - **`--short`** (optional flag): also/instead produce a tighter one-liner.
14
-
15
- If the type is missing or ambiguous, ask me once; otherwise proceed without questions.
16
-
17
- ## Steps
18
- 1. Resolve the target branch (default `origin/master`). Run:
19
- - `git log <target>..HEAD --oneline --no-merges`
20
- - `git diff <target>...HEAD --stat`
21
- - `git diff <target>...HEAD -- <the changed source files>` (skip `dist/`, lockfiles, version-only bumps)
22
- 2. Read the actual source diff to understand the real user/merchant-facing impact — do not
23
- paraphrase the commit subject; describe what changed and why it matters to the user.
24
- 3. Pull the Shortcut ID (`sc-XXXXXX`) from the branch name or commit subjects.
25
- 4. Output ONE sentence (may use commas/clauses), prefixed with `[sc-XXXXXX]`.
26
-
27
- ## House style by type
28
-
29
- **Fix** — state the symptom that was broken, then the fix.
30
- Open with "Fixed" or "Fixes".
31
- > [sc-338198] Fixed selfie auto-capturing off-center or too-close faces by adding real-time centering and distance guards that block capture until the face is properly framed.
32
-
33
- **Enhancement** — improves existing behavior. Open with an action verb ("Changed",
34
- "Increased", "Skipped") or "<feature> now ...".
35
- > [sc-326139] Increased camera capture area size to enhance resolution of submitted document images.
36
-
37
- **New** — a brand-new capability. Open with "Introduced", "Added", or "Implemented".
38
- > [sc-334995] Real-time card edge detection that automatically captures the document the moment its edges are properly aligned within the frame, with live on-screen feedback so users no longer have to tap the capture button themselves.
39
-
40
- ## Tone rules
41
- - User/merchant-facing outcome first, mechanism second. Not a changelog of files touched.
42
- - One sentence. Concrete (name the feature/page/behavior), not abstract.
43
- - No em dash "—"; use a colon or comma.
44
- - No bold, headers, emoji, or bullet lists in the message itself.
45
- - If `--short`, also give a tighter variant that keeps the `[sc-XXXXXX]` prefix and the core outcome.
46
-
47
- After the message, offer light follow-ups (shorter version, different type framing) rather than over-producing.
package/.env.development DELETED
@@ -1,3 +0,0 @@
1
- FACE_MODEL=/face-models
2
- CARD_MODEL=/card-models
3
- ORT=/ort
package/.env.production DELETED
@@ -1,3 +0,0 @@
1
- FACE_MODEL=https://assets.pipwave.com/ekyc/sdk/face-models
2
- CARD_MODEL=https://assets.pipwave.com/ekyc/sdk/card-models
3
- ORT=https://assets.pipwave.com/ekyc/sdk/ort
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- 22
@@ -1,2 +0,0 @@
1
- import PWUISDK from "./pw-bundle.js";
2
- export default PWUISDK;
@@ -1,46 +0,0 @@
1
- # eKYC Web UI review — sc-361686 (autofocus rear-lens selection for document capture) — simple version
2
-
3
- **Counts:** 0 Critical · 1 Major · 4 Minor · 1 Enhancement · **6 total**
4
-
5
- Project: repo `pw-ekyc-web-ui`, npm package `pipwave-ekyc-uikit`.
6
- MR: https://gitlab.g2g.com/pipwave-frontend/pw-ekyc-web-ui/-/merge_requests/110 (target `release-beta`)
7
- Reviewed at: `816d8dfd445c7a01de745dd30b63ae9f11b409ef`
8
- Round: 1.
9
-
10
- Local working document — not to be committed.
11
-
12
- Scope note: the debug-badge clipping point (`max-height:45%; overflow:hidden` on the image-debug trail) was reviewed and deliberately left out of this round.
13
-
14
- ## What the author was trying to do
15
-
16
- On some Samsung phones the browser gives document capture a rear lens that cannot focus, so cards come out blurry. The new `cameraSelection.ts` helper checks whether the lens the browser picked can focus and, if not, briefly opens the other rear lenses to find one that can, switches to it, and remembers it in `localStorage`. Because it has to let go of a working camera to do that, the MR adds retries and a Retry/Cancel dialog in `card-camera.ts` for the case where the camera does not come back.
17
-
18
- ## Major
19
-
20
- ### 1. [src/plugins/card-camera.ts:764](src/plugins/card-camera.ts#L764), [src/plugins/card-camera.ts:816](src/plugins/card-camera.ts#L816)
21
-
22
- Tapping Retry on the "camera unavailable" dialog calls `loadVideoStream()` again, and if the first `getUserMedia` fails again the error is not a `RearCameraReopenError`, so it only goes to `console.error`. The user tapped Retry and gets a black screen with no dialog. Fix: on a Retry, route any failure (other than permission denied) back into `recoverCameraFailure`.
23
-
24
- ## Minor
25
-
26
- ### 2. [src/helper/cameraSelection.ts:401](src/helper/cameraSelection.ts#L401), [src/helper/cameraSelection.ts:421](src/helper/cameraSelection.ts#L421)
27
-
28
- If the remembered lens fails to open for a one-off reason (camera still busy, `NotReadableError`), it is forgotten anyway, so the next visit runs the whole slow probe cycle again. Fix: open it with `openWithRetry` and only forget it on `OverconstrainedError` / `NotFoundError`.
29
-
30
- ### 3. [src/helper/cameraSelection.ts:480](src/helper/cameraSelection.ts#L480)
31
-
32
- `stopStream(defaultStream)` runs before checking whether any lens actually needs probing. If a browser ever reports focus info at device level, the working camera is dropped and reopened for nothing. Fix: only stop the default when a candidate is `unknown` or a replacement has been chosen.
33
-
34
- ### 4. [src/plugins/card-camera.ts:269](src/plugins/card-camera.ts#L269)
35
-
36
- `init()` hides the `preparing_camera` spinner straight away, so during the new multi-step camera start-up (which can take a few seconds on the A15) the user just sees a black viewport. Fix: hide the spinner when the stream attaches or the recovery dialog shows.
37
-
38
- ### 5. [src/plugins/card-camera.ts:812](src/plugins/card-camera.ts#L812), [src/translations/en.ts:167](src/translations/en.ts#L167)
39
-
40
- The dialog reuses `face_camera_unavailable`, which says "Check your connection", but this failure has nothing to do with the connection. Fix: add a `doc_camera_unavailable` key in `en.ts` with camera-only wording.
41
-
42
- ## Enhancement
43
-
44
- ### 6. [src/plugins/card-camera.ts:772](src/plugins/card-camera.ts#L772), [src/plugins/card-camera.ts:256](src/plugins/card-camera.ts#L256), [src/plugins/card-camera.ts:534](src/plugins/card-camera.ts#L534)
45
-
46
- The new `armIdleTimer()` is the same code as the idle-timer blocks still sitting in `init()` and `resumeVideoStream()`. Fix: make those two call `armIdleTimer()`.
@@ -1,84 +0,0 @@
1
- # eKYC Web UI review — sc-361686 (autofocus rear-lens selection for document capture)
2
-
3
- **Counts:** 0 Critical · 1 Major · 4 Minor · 1 Enhancement · **6 total**
4
-
5
- Project: repo `pw-ekyc-web-ui`, npm package `pipwave-ekyc-uikit`.
6
- MR: https://gitlab.g2g.com/pipwave-frontend/pw-ekyc-web-ui/-/merge_requests/110 (target `release-beta`)
7
- Reviewed at: `816d8dfd445c7a01de745dd30b63ae9f11b409ef`
8
- Round: 1 (no existing threads on the MR). All 6 findings posted and resolved; round 2 is in
9
- `branch-review-sc-361686-round-2.md`.
10
-
11
- Local working document — not to be committed.
12
-
13
- Scope note: the debug-badge clipping point (`max-height:45%; overflow:hidden` on the image-debug trail) was reviewed and deliberately left out of this round.
14
-
15
- ## What this change does and why
16
-
17
- Document capture asked the browser for any rear camera, and on some multi-lens Androids (Galaxy A15 confirmed, S-series reported) the browser hands back a fixed-focus secondary lens, so a card at guide distance is always blurred. This change adds a `cameraSelection` helper that opens the browser's default, reads `getCapabilities().focusMode` on the live track, and only when that lens is provably fixed-focus and another rear lens provably autofocuses does it switch to that lens, request continuous focus, and remember the choice in `localStorage` for the next visit. Everything else (iOS Safari, Firefox, desktop webcams, single rear lens) keeps the default stream untouched.
18
-
19
- Because Chromium exposes `focusMode` only on live tracks, the helper has to release the default stream and briefly open each other rear lens to read its capabilities, then reopen whichever lens wins. That release-and-reopen cycle is where the risk sits: a camera that was working is let go and may not come back on a slow HAL. The MR handles that with retries, a dedicated `RearCameraReopenError`, and a recovery dialog in `card-camera.ts`. Most of the findings below are about the edges of that cycle — what happens when the recovery itself fails, when a remembered lens fails to open for a transient reason, and how long the viewport stays blank while the cycle runs.
20
-
21
- ## Verified and found correct
22
-
23
- - `tsc --noEmit` clean; `vitest run` on Node 22 gives 150 passing across 8 files, matching the description.
24
- - Stream ownership: every path in `openRearCameraStream` stops the streams it does not return (default, probe, replacement, remembered), including the throw paths. The `finally` blocks with the `stream = null` ownership hand-off are correct.
25
- - `RearCameraReopenError` is declared below its first use, but both uses are inside an `async` function body that runs after module evaluation, so there is no temporal-dead-zone issue.
26
- - The debug badge is appended inside the camera div (`ELEMENT_IDS.BODY`), so `stopVideoStream()` removes it along with the camera. It does not outlive the camera view.
27
- - The recovery dialog's Cancel tears the camera down with `stopVideoStream()`, which matches the face camera's `showModelLoadFailedDialog` behaviour.
28
- - `DocCaptureDetail.camera` is optional and the `captureImage` `CustomEvent` is consumed only by `VerifyId`, so the model change is not a consumer-facing contract change.
29
- - The label regex fallback in `isRearFacing` only matters where focus capabilities exist (Chromium), where labels are always of the form `camera N, facing back`.
30
- - The `package.json` / `package-lock.json` 3.8.0 bump is master's state: `release-beta` is one merge ahead of the MR base and the branch was cut from master, so merging brings master's version commit into `release-beta` as the description says.
31
-
32
- ## Major
33
-
34
- ### 1. Retry from the recovery dialog can fail silently
35
-
36
- [src/plugins/card-camera.ts:764](src/plugins/card-camera.ts#L764), [src/plugins/card-camera.ts:816](src/plugins/card-camera.ts#L816)
37
-
38
- The recovery dialog's Retry calls `loadVideoStream()` again, which runs `openRearCameraStream` from the top. By that point the remembered lens has been forgotten (`writeRemembered(storage, null)` ran before the reopen error), so the first request is the plain `mediaDevices.getUserMedia({ video: constraints })` at [src/helper/cameraSelection.ts:433](src/helper/cameraSelection.ts#L433) with no retry and no `RearCameraReopenError` wrapping. If the HAL is still busy it rejects with `NotReadableError`, the `.catch` sees a name other than `RearCameraReopenError`, and the only effect is `console.error`. The dialog has already closed, the viewport stays black, and the re-armed idle timer shows the idle message 90 seconds later. The Retry the user was told to tap does nothing visible when it fails.
39
-
40
- Suggest passing a `recovering` flag into `loadVideoStream` so a failure on a user-initiated Retry re-enters `recoverCameraFailure` for any error name other than `NotAllowedError` / `SecurityError` / `NotFoundError`, or having `openRearCameraStream` use `openWithRetry` for the very first default open as well and reporting a `NotReadableError` there as a reopen-class failure.
41
-
42
- ## Minor
43
-
44
- ### 2. A remembered lens is forgotten on any open error, including transient ones
45
-
46
- [src/helper/cameraSelection.ts:401](src/helper/cameraSelection.ts#L401), [src/helper/cameraSelection.ts:421](src/helper/cameraSelection.ts#L421)
47
-
48
- The remembered-device open is a plain `getUserMedia` with no retry, and the `catch` forgets the stored id on every error. A `NotReadableError` from a HAL that has not finished releasing the previous camera is transient, but it still wipes the remembered lens, so the next visit repeats the full default → release → probe → reopen cycle, which is exactly the path this MR works hardest to avoid. Only `OverconstrainedError` / `NotFoundError` actually mean the id is stale.
49
-
50
- Suggest opening the remembered lens through `openWithRetry(…, [retryDelayMs], log)` and forgetting it only when the final error name is in the stale set (`OverconstrainedError`, `NotFoundError`), or at minimum keeping the stored id on `NotReadableError` / `AbortError` so a one-off busy camera does not cost the next visit a probe cycle.
51
-
52
- ### 3. The default stream is released before it is known that a probe is needed
53
-
54
- [src/helper/cameraSelection.ts:480](src/helper/cameraSelection.ts#L480)
55
-
56
- `stopStream(defaultStream)` runs unconditionally before `probeFocusByOpening`, but that function only opens candidates whose focus is still `unknown`. When device-level capabilities already carry `focusMode` (the `deviceCapabilitiesIncludeFocus` test case), nothing is probed, and if `chooseReplacement` then returns `null` the working default has been released for no reason and must be reopened, taking on the reopen risk this MR otherwise guards against. Latent today because Chromium does not expose focus at device level, but silent if a browser starts doing so.
57
-
58
- Suggest releasing the default only when at least one candidate is `unknown`, and otherwise running `chooseReplacement` first and stopping the default only once a replacement has been chosen.
59
-
60
- ### 4. Nothing on screen covers the longer camera start-up
61
-
62
- [src/plugins/card-camera.ts:269](src/plugins/card-camera.ts#L269)
63
-
64
- `init()` hides the `preparing_camera` overlay synchronously in its `finally`, so the user sees the camera UI with a black viewport until the stream attaches. That was one `getUserMedia` before; on the A15 path it is now three opens plus up to 350 ms of probe retry and up to 1400 ms of reopen retry, and on the failure path a further 1050 ms before the recovery dialog appears. In scope only because this change is what lengthens the gap.
65
-
66
- Suggest keeping the `preparing_camera` overlay up until `videoElement.srcObject` is set or the recovery dialog renders, and calling `hideFullLoading(this.cameraId)` from those two points instead of from `init()`.
67
-
68
- ### 5. The recovery dialog's copy blames the connection
69
-
70
- [src/plugins/card-camera.ts:812](src/plugins/card-camera.ts#L812), [src/translations/en.ts:167](src/translations/en.ts#L167)
71
-
72
- `face_camera_unavailable` reads "Couldn't set up the camera. Check your connection and tap Retry." That wording was written for the face model download. Here the failure is a camera the HAL would not hand back, and connectivity has nothing to do with it, so the advice points the user at the wrong thing. `en.ts` is the only translation file, so a new key is cheap.
73
-
74
- Suggest a dedicated key such as `doc_camera_unavailable: "Couldn't start the camera. Tap Retry to try again."`, or dropping the connection sentence from a shared key if the face camera can live without it.
75
-
76
- ## Enhancement
77
-
78
- ### 6. `armIdleTimer()` is added but the two existing idle-timer blocks still inline the same code
79
-
80
- [src/plugins/card-camera.ts:772](src/plugins/card-camera.ts#L772), [src/plugins/card-camera.ts:256](src/plugins/card-camera.ts#L256), [src/plugins/card-camera.ts:534](src/plugins/card-camera.ts#L534)
81
-
82
- The new helper is a copy of the `setTimeout` block in `init()` and `resumeVideoStream()`, so the same dialog is now built in three places. A future change to the idle behaviour has three sites to keep in step.
83
-
84
- Consider having `init()` and `resumeVideoStream()` call `this.armIdleTimer()` in place of their inline blocks, since the helper already clears any existing timer first.
@@ -1,28 +0,0 @@
1
- # eKYC Web UI review — sc-361686 (autofocus rear-lens selection for document capture) — simple version
2
-
3
- **Counts:** 0 Critical · 1 Major · 0 Minor · 0 Enhancement · **1 total**
4
-
5
- Project: repo `pw-ekyc-web-ui`, npm package `pipwave-ekyc-uikit`.
6
- MR: https://gitlab.g2g.com/pipwave-frontend/pw-ekyc-web-ui/-/merge_requests/110 (target `release-beta`)
7
- Reviewed at: `00c9b9c62d13266f95ade907d542a589b0948075`
8
- Round: 2. Round 1 is in `branch-review-sc-361686-round-1-simple.md`.
9
-
10
- Local working document — not to be committed.
11
-
12
- ## What the author was trying to do
13
-
14
- Round 1 flagged six things about how the multi-lens camera picker (`cameraSelection.ts`) and the failure-recovery dialog in `card-camera.ts` handle edge cases. This round's fixes address all six, but the fix for the "Retry button does nothing" finding was to remove the Retry dialog entirely (a product call: no new UI for this fix). That trade is where this round's one new finding comes from.
15
-
16
- ## How the round-1 findings were addressed
17
-
18
- Checked against `00c9b9c6`. All 6 round-1 threads are resolved on the MR.
19
-
20
- - **Fixed:** 2, 3, 4, 5. Each matches the suggested fix and has a new test covering it (remembered lens survives a busy retry, default camera isn't released when no probe is needed, the loading spinner stays up until the stream attaches or the recovery flow finishes, the "check your connection" wording is gone with the dialog).
21
- - **Fixed differently — see finding 1:** 1. Instead of making the dialog's Retry button work, `card-camera.ts` removes the dialog and does one silent retry instead. That's a valid fix for "Retry does nothing" but it opens a new problem, described below.
22
- - **Accepted:** 6. `armIdleTimer()` was removed along with the dialog, so the duplication it was about no longer exists. Consolidating the two remaining idle-timer blocks was declined for this MR; offered as a follow-up.
23
-
24
- ## Major
25
-
26
- ### 1. [src/plugins/card-camera.ts:793](src/plugins/card-camera.ts#L793), [src/plugins/card-camera.ts:817](src/plugins/card-camera.ts#L817)
27
-
28
- When the camera can't be gotten back after the app let go of it, there is now no dialog and no button at all, just a `console.error` (phones have no console, so nobody sees it). The screen stays black until the existing 90-second idle timer kicks in and shows "idle" — which throws the user back to the start of capture, not just a camera retry. Fix: show some visible "camera unavailable" message with a way to back out as soon as the one silent retry fails, or shorten the idle timer specifically for this failure so 90 seconds isn't the only way out.
@@ -1,51 +0,0 @@
1
- # eKYC Web UI review — sc-361686 (autofocus rear-lens selection for document capture)
2
-
3
- **Counts:** 0 Critical · 1 Major · 0 Minor · 0 Enhancement · **1 total**
4
-
5
- Project: repo `pw-ekyc-web-ui`, npm package `pipwave-ekyc-uikit`.
6
- MR: https://gitlab.g2g.com/pipwave-frontend/pw-ekyc-web-ui/-/merge_requests/110 (target `release-beta`)
7
- Reviewed at: `00c9b9c62d13266f95ade907d542a589b0948075`
8
- Round: 2. Round 1 (26 threads: 6 findings + 20 system notes, all 6 finding threads resolved) is in
9
- `branch-review-sc-361686-round-1.md`.
10
-
11
- Local working document — not to be committed.
12
-
13
- ## What this change does and why
14
-
15
- Document capture asked the browser for any rear camera, and on some multi-lens Androids (Galaxy A15 confirmed, S-series reported) the browser hands back a fixed-focus secondary lens, so a card at guide distance is always blurred. The `cameraSelection` helper opens the browser's default, reads `getCapabilities().focusMode` on the live track, and only when that lens is provably fixed-focus and another rear lens provably autofocuses does it switch to that lens, request continuous focus, and remember the choice in `localStorage`.
16
-
17
- Round 1 flagged five findings around the release-and-reopen cycle this requires, plus one duplication nit. This round's commits (`4a240fd`, `ba707c6`, `00c9b9c`) address all six, but the fix for the recovery-dialog finding took the shape of removing the dialog rather than repairing its Retry button: on the product's decision, a failed reopen is now handled with a single silent retry and no user-facing dialog at all. That trade removes the broken Retry button, but it also removes the only feedback the user ever got when a reopen fails outright, which is where this round's one finding sits.
18
-
19
- ## How the round-1 findings were addressed
20
-
21
- Verified against `00c9b9c6`. "Fixed" means the code was checked, not the reply. All 6 round-1 threads are resolved on the MR.
22
-
23
- | # | Round-1 finding | Fix commit | How it was addressed | Status |
24
- |---|---|---|---|---|
25
- | 1 | Retry from the recovery dialog can fail silently | `4a240fd`, `ba707c6`, `00c9b9c` | The recovery dialog and its Retry button are removed. A reopen-class failure now gets one silent plain reopen after the default has settled, and a cold default open also retries once on a transient error. Route differs from the suggestion (no `recovering` flag; the dialog is gone rather than repaired). | Fixed differently — see finding 1 |
26
- | 2 | A remembered lens is forgotten on any open error, including transient ones | `4a240fd` | The remembered-device open now goes through `openWithRetry`, and the stored id is forgotten only on `OverconstrainedError` / `NotFoundError`; a `NotReadableError` keeps it. Matches the suggested fix, confirmed by two new tests and `storage.removeItem` assertions. | Fixed |
27
- | 3 | The default stream is released before it is known that a probe is needed | `4a240fd` | A `needsProbe` check now runs before `stopStream(defaultStream)`; when every candidate's focus is already known, `chooseReplacement` runs first and the default is stopped only if a replacement is chosen. Covered by a new test with two device-level-fixed lenses. | Fixed |
28
- | 4 | Nothing on screen covers the longer camera start-up | `4a240fd`, `ba707c6` | `init()` no longer hides `preparing_camera` in a `finally`; the overlay now hides from `loadVideoStream`'s success, failure and cancellation branches, each guarded by `stale()` so a torn-down instance cannot clear a newer instance's spinner. | Fixed |
29
- | 5 | The recovery dialog's copy blames the connection | `4a240fd` | Removed along with the dialog. `face_camera_unavailable` is confirmed referenced only by `face-camera.ts` again. | Fixed |
30
- | 6 | `armIdleTimer()` duplicates the two inline idle-timer blocks | `4a240fd`, `ba707c6` | The helper was removed with the dialog, so the codebase is back to the original two inline blocks; `resumeVideoStream` now clears the timer before arming so the two cannot stack. Consolidation declined for this MR, offered as a follow-up. | Accepted |
31
-
32
- ## Verified and found correct
33
-
34
- - `tsc --noEmit` clean; `vitest run` on Node 22 gives 156 passing across 8 files (up from 150 in round 1).
35
- - Production build (`npm run build`) completes with only the pre-existing scss/bundle-size warnings, no new ones.
36
- - `SelectionCancelledError` is threaded through every retry and probe path and checked before each is wrapped as a `RearCameraReopenError`, so a torn-down view during selection neither leaks a stream nor gets misreported as a reopen failure. Covered by the new cancellation test.
37
- - `isFatalCameraError` / `isRetryableCameraError` partition cleanly: a describe failure (`TypeError`) is correctly treated as retryable-as-a-reopen but not fatal, per the new test's own comment.
38
- - `reopenTimer` is cleared in `_cleanup()`, and every continuation off it (`.then`/`.catch`/`.finally`) is guarded by the same `stale()` check as the main selection promise, so a stream opened after teardown is always stopped rather than attached.
39
- - The `safeLocalStorage` write-probe was dropped, but `readRemembered`/`writeRemembered` already wrap every `getItem`/`setItem`/`removeItem` call individually, so a storage object that throws on write behaves the same as before.
40
-
41
- ## Major
42
-
43
- ### 1. A reopen-class camera failure now leaves the viewport black with no feedback for up to 90 seconds
44
-
45
- [src/plugins/card-camera.ts:793](src/plugins/card-camera.ts#L793), [src/plugins/card-camera.ts:817](src/plugins/card-camera.ts#L817)
46
-
47
- Posted: reply on discussion `a95954b2ebc988e7c010bc15af664778979b19db` (the round-1 recovery-dialog thread) · reopened.
48
-
49
- With the recovery dialog removed, a `RearCameraReopenError` with a fatal reason (`NotAllowedError`/`SecurityError`/`NotFoundError`/`OverconstrainedError`) hits `hideFullLoading` and returns with nothing else shown; a non-fatal reason gets exactly one silent plain reopen, and if that also fails the `.catch` only does `console.error`. Either way the user is left looking at a blank camera viewport with no dialog, no retry affordance and no explanation, until the pre-existing 90-second idle timer fires `stopVideoStream()` and shows the generic idle message — which restarts the whole capture flow rather than just the camera. Phones have no console, so there is no way for anyone but a developer with devtools attached to tell why the screen went black. This is a step down from both the pre-MR behaviour (single `getUserMedia`, so a failure was the original permission/no-camera rejection surfacing to whatever handled it before) and round 1's dialog (which at least told the user something and offered Cancel).
50
-
51
- Suggest showing a plain "camera unavailable" message with a way back (e.g. `stopVideoStream()`) when the silent reopen's own `.catch` fires, even without a Retry button, or re-arming the idle timer at a much shorter interval specifically for this path so the existing idle dialog surfaces sooner than 90 seconds.
@@ -1,26 +0,0 @@
1
- # eKYC Web UI review — sc-361686 (autofocus rear-lens selection for document capture) — simple version
2
-
3
- **Counts:** 0 Critical · 0 Major · 0 Minor · 1 Enhancement · **1 total**
4
-
5
- Project: repo `pw-ekyc-web-ui`, npm package `pipwave-ekyc-uikit`.
6
- MR: https://gitlab.g2g.com/pipwave-frontend/pw-ekyc-web-ui/-/merge_requests/110 (target `release-beta`)
7
- Reviewed at: `53d741920a7680cc49c32aa6ca4509e99ddc3b2b`
8
- Round: 3. Round 2 is in `branch-review-sc-361686-round-2-simple.md`.
9
-
10
- Local working document — not to be committed.
11
-
12
- ## What the author was trying to do
13
-
14
- Round 2 flagged one problem: when the camera can't be recovered, the screen just goes black with no message. This round adds a proper fallback: the camera now fires a `cameraUnavailable` event when it truly can't recover, and the app sends the user to the same "Something went wrong" page an upload failure already shows.
15
-
16
- ## How the round-2 finding was addressed
17
-
18
- Checked against `53d7419`. The thread is resolved.
19
-
20
- - **Fixed:** 1. `card-camera.ts` now emits `cameraUnavailable` on every unrecoverable-camera case, and `VerifyId.ts` / `VerifyCc.ts` catch it, stop the camera properly, and show the generic error page. While fixing this, a related gap was also closed: a permission error happening right after a remembered lens was released used to slip through as an ordinary error (no error page shown); that path now also counts as unrecoverable.
21
-
22
- ## Enhancement
23
-
24
- ### 1. [src/helper/cameraSelection.ts:226](src/helper/cameraSelection.ts#L226)
25
-
26
- The helper function `isRetryableCameraError` is exported but nothing in the actual app calls it anymore after this round's fix; only its own test still uses it. Fix: delete it (and its two test assertions), or inline what it checks where it used to be called.
@@ -1,41 +0,0 @@
1
- # eKYC Web UI review — sc-361686 (autofocus rear-lens selection for document capture)
2
-
3
- **Counts:** 0 Critical · 0 Major · 0 Minor · 1 Enhancement · **1 total**
4
-
5
- Project: repo `pw-ekyc-web-ui`, npm package `pipwave-ekyc-uikit`.
6
- MR: https://gitlab.g2g.com/pipwave-frontend/pw-ekyc-web-ui/-/merge_requests/110 (target `release-beta`)
7
- Reviewed at: `53d741920a7680cc49c32aa6ca4509e99ddc3b2b`
8
- Round: 3. Round 2 (1 finding, posted as a reply on the round-1 thread, now resolved) is in
9
- `branch-review-sc-361686-round-2.md`.
10
-
11
- Local working document — not to be committed.
12
-
13
- ## What this change does and why
14
-
15
- Round 2's one finding was that a reopen-class camera failure — the app had already released a working camera and could not get one back — left the viewport black with no feedback at all, since round 2's own commits had removed the recovery dialog as a product decision. This round's two commits (`fd049bf`, `53d7419`) close that gap: the camera plugin now emits a `cameraUnavailable` event on every reopen-class failure (fatal reason, failed silent reopen, or a reopen failure discovered only after a remembered lens was released), and `VerifyId`/`VerifyCc` route it to the same generic "Something went wrong" page an upload failure already shows, after tearing the camera down. The change is additive and narrowly scoped to the failure path, so this round has nothing left to raise beyond a small dead-code nit the refactor left behind.
16
-
17
- ## How the round-2 findings were addressed
18
-
19
- Verified against `53d7419`. The round-2 thread (reply on discussion `a95954b2ebc988e7c010bc15af664778979b19db`) is resolved.
20
-
21
- | # | Round-2 finding | Fix commit | How it was addressed | Status |
22
- |---|---|---|---|---|
23
- | 1 | A reopen-class camera failure now leaves the viewport black with no feedback for up to 90 seconds | `fd049bf`, `53d7419` | `card-camera.ts` gained `emitCameraUnavailable`/`listenToCameraUnavailable`, wired into both the fatal-reason branch and the failed-silent-reopen branch. `VerifyId` and `VerifyCc` each register a listener before `init()` that calls `stopVideoStream()` then `openErrorPage()`. A third gap the fix also closed: a fatal error after a *released remembered* lens was previously falling through as an ordinary first-open error (missed the `isFatalCameraError` check entirely) — `cameraSelection.ts:511` now treats every failure after `releasedCamera` as reopen-class, with a new test pinning it. | Fixed |
24
-
25
- ## Verified and found correct
26
-
27
- - `tsc --noEmit` clean; `vitest run` on Node 22 gives 157 passing across 8 files (up from 156). Production build clean, same pre-existing scss/bundle-size warnings only.
28
- - Both `cameraDiv` elements (`VerifyId.ts:272`, `VerifyCc.ts:30`) are created and appended before `listenToCameraUnavailable` is called, so `document.getElementById(this.cameraId)` resolves at registration time — the listener does get attached despite being wired before `init()`.
29
- - `emitCameraUnavailable`'s two call sites are both past the shared `stale()`/`SelectionCancelledError` guard at the top of the `.catch`, so a torn-down view never fires a stray event, and the listener's own `stopVideoStream()` bumping `streamGeneration` makes the subsequent `.finally()`'s `hideFullLoading` a no-op rather than a double call.
30
- - `openErrorPage()` is the same generic error screen already used for upload failures (`VerifyId.ts:544`, `VerifyCc.ts:183`), called with no arguments so the raw camera error is logged via `warn()` but never forwarded to the merchant, matching the stated intent.
31
- - Removing the `isRetryableCameraError` gate on the default-open catch doesn't reopen the `SelectionCancelledError` mis-wrapping risk it looks like it would: cancellation only ever happens after `stale()` is already true, and the `.catch` in `card-camera.ts` checks `stale()` first regardless of how the error got wrapped.
32
-
33
- ## Enhancement
34
-
35
- ### 1. `isRetryableCameraError` is exported but no longer called from production code
36
-
37
- [src/helper/cameraSelection.ts:226](src/helper/cameraSelection.ts#L226)
38
-
39
- This round's fix at [src/helper/cameraSelection.ts:511](src/helper/cameraSelection.ts#L511) dropped the only production call site (`releasedCamera && isRetryableCameraError(err)` became `releasedCamera`); `openWithRetry`'s own retry decision uses `NON_RETRYABLE.has(name)` directly and never called this helper. It now exists only for its own two assertions in `cameraSelection.test.ts`.
40
-
41
- Suggest removing the export (and its two direct assertions) if nothing else is meant to call it, or inlining the two `NON_RETRYABLE` checks it currently duplicates.
@@ -1,25 +0,0 @@
1
- # pipwave-ekyc-uikit 3.8.1-beta.1 release drafts
2
-
3
- ## #qa-staging-release: hosted page (SENT)
4
-
5
- Deploying pipwave Hosted Page
6
- • [sc-361686] Install pipwave-ekyc-uikit 3.8.1-beta.1 @Che Chin
7
-
8
- Sent: https://hydron-holdings.slack.com/archives/C06C8D76203/p1790064664937059
9
-
10
- ---
11
-
12
- ## #qa-staging-release: demo (SENT)
13
-
14
- Deploying pipwave Demo
15
- • [sc-361686] Select an autofocus rear lens for document capture on multi-lens Android @Jia Chong Ong
16
-
17
- Sent: https://hydron-holdings.slack.com/archives/C06C8D76203/p1790064670138489
18
-
19
- Note: sc-359303 (SDK bump) was left off this post at the user's request.
20
-
21
- ---
22
-
23
- ## Testing-request DM
24
-
25
- (pending recipient confirmation)
@@ -1,65 +0,0 @@
1
- # pipwave-ekyc-uikit 3.8.1 release drafts
2
-
3
- ## #hello-serverless: eKYC Web UI (SENT)
4
-
5
- pipwave eKYC Web UI Hotfix 3.8.1 is ready:
6
- • MR
7
- https://gitlab.g2g.com/pipwave-frontend/pw-ekyc-web-ui/-/merge_requests/112
8
- • Release Notes
9
- https://gitlab.g2g.com/pipwave-frontend/pw-ekyc-web-ui/-/wikis/**Release-3.8-Deployment-Notes#hotfix-381
10
- cc: @Jia Chong Ong
11
-
12
- Sent: https://hydron-holdings.slack.com/archives/C017T2JCYNT/p1790135781826829
13
-
14
- Thread reply when the deploy starts (MANUAL, post on the message above):
15
-
16
- deploying
17
-
18
- ---
19
-
20
- ## #hello-serverless: Hosted Page (SENT)
21
-
22
- pipwave Hosted Page Hotfix 1.0.20 is ready:
23
- • MR
24
- https://gitlab.g2g.com/pipwave-frontend/pw-hosted-page/-/merge_requests/93
25
- • Release Notes
26
- https://gitlab.g2g.com/pipwave-frontend/pw-hosted-page/-/wikis/*Release-1.0#hotfix-1020
27
-
28
- Sent: https://hydron-holdings.slack.com/archives/C017T2JCYNT/p1790136773323409
29
-
30
- Thread reply when the deploy starts (MANUAL):
31
-
32
- deploying
33
-
34
- ---
35
-
36
- ## #release-news: eKYC Web UI (SENT)
37
-
38
- **@here pipwave eKYC Web UI Hotfix 3.8.1 Deployment**
39
- **Fixes** :ladybug:
40
- • [sc-361686] On certain multi-lens Android phones (e.g. Samsung Galaxy A15) document capture used a fixed-focus rear lens and produced blurry ID images; it now selects an autofocus rear lens. Other devices are unchanged. @Jia Chong Ong
41
-
42
- Sent: https://hydron-holdings.slack.com/archives/CD0KJ3UBT/p1790137532124269
43
-
44
- ---
45
-
46
- ## #release-news: G2G install thread reply (MANUAL, on the post above)
47
-
48
- @Wai Nam @Moh Lun Chan need help to install latest version in G2G. :csc_thanks:
49
- cc @Archy
50
-
51
- ---
52
-
53
- ## #release-news: Hosted Page (SENT)
54
-
55
- **@here pipwave Hosted Page Hotfix 1.0.20 Deployment**
56
- **Enhancements** :technologist:
57
- • [sc-361686] Install pipwave-ekyc-uikit 3.8.1 @Che Chin
58
-
59
- Sent: https://hydron-holdings.slack.com/archives/CD0KJ3UBT/p1790137537548379
60
-
61
- ---
62
-
63
- ## Testing-request DM
64
-
65
- (pending recipient confirmation)
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env node
2
- // Project verification target — the single interface the Stop hook (and humans) call.
3
- // The hook is generic and language-agnostic; THIS file owns which commands mean "verified"
4
- // for pw-ekyc-web-ui. Add, remove, or reorder steps here — the hook never changes.
5
- //
6
- // npm run verify # run it by hand
7
- //
8
- // Steps run in order and stop at the first failure. Exit 0 = verified, non-zero = failed.
9
- import { spawnSync } from "node:child_process";
10
-
11
- const steps = [
12
- { name: "typecheck", cmd: "npx", args: ["tsc", "--noEmit"] },
13
- // Coverage provider + thresholds + fileParallelism:false live in vitest.config.ts.
14
- // (Sequential files keep coverage instrumentation from interleaving across files.)
15
- { name: "unit + integration tests (coverage-gated)", cmd: "npx", args: ["vitest", "run", "--coverage"] },
16
- ];
17
-
18
- let failed = null;
19
- for (const s of steps) {
20
- process.stdout.write(`\n=== verify: ${s.name} ===\n`);
21
- const r = spawnSync(s.cmd, s.args, { stdio: "inherit", shell: true });
22
- if (r.status !== 0) {
23
- process.stdout.write(`\n[FAIL] ${s.name} (exit ${r.status ?? "signal " + r.signal})\n`);
24
- failed = s.name;
25
- break;
26
- }
27
- process.stdout.write(`[OK] ${s.name}\n`);
28
- }
29
-
30
- if (failed) {
31
- process.stdout.write(`\nVerification FAILED at: ${failed}\n`);
32
- process.exit(1);
33
- }
34
- process.stdout.write(`\nVerification PASSED (${steps.map((s) => s.name).join(", ")})\n`);
35
- process.exit(0);
package/tsconfig.esm.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "module": "es2020"
5
- },
6
- "files": ["src/plugins/card-detection.inference.ts"]
7
- }
package/vitest.config.ts DELETED
@@ -1,19 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- environment: "node",
6
- include: ["src/**/*.test.ts"],
7
- fileParallelism: false,
8
- coverage: {
9
- provider: "v8",
10
- reporter: ["text", "json-summary"],
11
- thresholds: {
12
- lines: 62,
13
- statements: 61,
14
- functions: 58,
15
- branches: 52,
16
- },
17
- },
18
- },
19
- });