blocfeed 0.7.2 → 0.8.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/CHANGELOG.md +43 -0
- package/README.md +161 -2
- package/dist/chunk-KJRV7PRA.js +2 -0
- package/dist/chunk-ORVW2RTZ.cjs +2 -0
- package/dist/{controller-BPsU7cuY.d.cts → controller-BnTq9F8J.d.cts} +57 -3
- package/dist/{controller-BPsU7cuY.d.ts → controller-BnTq9F8J.d.ts} +57 -3
- package/dist/engine.cjs +1 -1
- package/dist/engine.d.cts +37 -3
- package/dist/engine.d.ts +37 -3
- package/dist/engine.js +1 -1
- package/dist/main.cjs +107 -4
- package/dist/main.d.cts +5 -2
- package/dist/main.d.ts +5 -2
- package/dist/main.js +107 -4
- package/package.json +4 -2
- package/dist/chunk-JVY6JTXP.cjs +0 -2
- package/dist/chunk-LAK7UUTC.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0 — 2026-02-24
|
|
4
|
+
|
|
5
|
+
### New features
|
|
6
|
+
|
|
7
|
+
- **Video recording** — Users can record a short screen capture clip alongside their feedback to demonstrate bug reproduction steps. Uses the browser's Screen Capture API (`getDisplayMedia`) with WebM output via `MediaRecorder`.
|
|
8
|
+
- Enabled via `config.recording.enabled: true` (off by default)
|
|
9
|
+
- Configurable max duration (`maxDurationMs`, default 30s) with auto-stop
|
|
10
|
+
- Configurable bitrate (`videoBitsPerSecond`, default 2.5 Mbps, ~9 MB for 30s)
|
|
11
|
+
- Codec negotiation: VP9 → VP8 → plain WebM
|
|
12
|
+
- Record button appears in review phase; pulsing red dot + timer during recording
|
|
13
|
+
- Video preview with playback controls before submission
|
|
14
|
+
- Direct upload to object storage via presigned URL (no server proxy)
|
|
15
|
+
- Auto-hidden on unsupported browsers (Safari, older browsers without `getDisplayMedia` + WebM `MediaRecorder`)
|
|
16
|
+
- **Programmatic recording API** — `startRecording()` and `stopRecording()` on `BlocFeedHandle` ref and `useBlocFeed()` hook
|
|
17
|
+
|
|
18
|
+
### Improvements
|
|
19
|
+
|
|
20
|
+
- New types exported: `RecordingConfig`, `VideoAsset`, `VideoIntent`, `VideoMime`.
|
|
21
|
+
- Engine exports: `startRecording`, `isRecordingSupported`, `RecordingSession` for headless usage.
|
|
22
|
+
- `BlocFeedStrings` extended with `recordButton`, `stopRecordButton`, `recordingText`, `recordingDeniedText`, `videoPreviewLabel`, `removeVideoButton`.
|
|
23
|
+
- Offline queue now strips video blobs (in addition to screenshots) before `localStorage` serialization.
|
|
24
|
+
- `SessionPhase` extended with `"recording"` phase.
|
|
25
|
+
- `BlocFeedError.kind` extended with `"recording_failed"`.
|
|
26
|
+
|
|
27
|
+
### Platform changes (blocfeed-frontend)
|
|
28
|
+
|
|
29
|
+
- New SQL migration `scripts/032_video_recording.sql` adds `video_url`, `video_mime`, `video_duration_ms`, `video_size_bytes`, `video_uploaded_at` columns with partial index.
|
|
30
|
+
- `POST /api/feedback` now accepts `video_intent`, inserts video metadata, and returns `upload_urls.video` presigned URL.
|
|
31
|
+
- New `POST /api/feedback/:id/video` fallback endpoint for uploading video blobs when presigned URLs are unavailable.
|
|
32
|
+
- Dashboard feedback detail panel shows a Video tab with `<video>` player when a recording is attached.
|
|
33
|
+
- Fixed `createPresignedUploadUrl` default content type from `"audio/webm"` to `"application/octet-stream"`.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 0.7.3 — 2026-02-23
|
|
38
|
+
|
|
39
|
+
### Improvements
|
|
40
|
+
|
|
41
|
+
- **Network diagnostics noise filter** — Analytics, tracking, and third-party service URLs are now automatically excluded from network capture. Built-in blocklist covers 50+ domains including Google Analytics, Facebook Pixel, Mixpanel, Amplitude, Segment, Hotjar, PostHog, Sentry, Intercom, HubSpot, Grammarly, reCAPTCHA, Stripe.js, cookie consent tools, and more.
|
|
42
|
+
- New `config.diagnostics.ignoreUrls` option to customize URL exclusions. Set to `[]` to disable the built-in blocklist and capture everything.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
3
46
|
## 0.7.1 — 2026-02-21
|
|
4
47
|
|
|
5
48
|
### New features
|
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ Drop-in in-app feedback widget for **Next.js** and **React**:
|
|
|
15
15
|
- **Dark / Light mode** — `"dark"`, `"light"`, or `"auto"` (follows system preference)
|
|
16
16
|
- **Conditional display** — show/hide widget by route pattern or custom predicate
|
|
17
17
|
- **Programmatic API** — `ref.open()`, `ref.close()`, `ref.submit(msg)` via React ref
|
|
18
|
+
- **Video recording** — record a short screen capture clip (via `getDisplayMedia`) to show bug reproduction steps
|
|
18
19
|
- **Secret leak detection** — scans client-side code for exposed API keys, database credentials, and tokens
|
|
19
20
|
|
|
20
21
|
Docs live in `docs/` (start at `docs/index.md`). Architecture pointers are in `ARCHITECTURE.md`.
|
|
@@ -129,6 +130,14 @@ All configuration is passed via the `config` prop on `<BlocFeedWidget>` or `<Blo
|
|
|
129
130
|
diagnostics: {
|
|
130
131
|
console: true,
|
|
131
132
|
network: true, // captures both fetch and XMLHttpRequest
|
|
133
|
+
// ignoreUrls: [], // override default blocked domains (see docs below)
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
// Video recording (screen capture)
|
|
137
|
+
recording: {
|
|
138
|
+
enabled: true,
|
|
139
|
+
maxDurationMs: 30_000,
|
|
140
|
+
videoBitsPerSecond: 2_500_000,
|
|
132
141
|
},
|
|
133
142
|
|
|
134
143
|
// Screenshot defaults
|
|
@@ -399,10 +408,77 @@ config={{
|
|
|
399
408
|
}}
|
|
400
409
|
```
|
|
401
410
|
|
|
402
|
-
Captured data is attached to `metadata._consoleLogs` and `metadata._networkErrors` in the payload.
|
|
411
|
+
Captured data is attached to `metadata._consoleLogs` and `metadata._networkErrors` in the payload. Messages and stacks are truncated at 2KB.
|
|
403
412
|
|
|
404
413
|
Both `fetch` and `XMLHttpRequest` are intercepted, so apps using axios, jQuery, or other XHR-based libraries get full network error capture.
|
|
405
414
|
|
|
415
|
+
### Network URL filtering (`ignoreUrls`)
|
|
416
|
+
|
|
417
|
+
By default, BlocFeed automatically filters out requests to analytics, tracking, and third-party service domains to keep diagnostics focused on relevant errors. The built-in blocklist includes 50+ domains:
|
|
418
|
+
|
|
419
|
+
| Category | Blocked domains |
|
|
420
|
+
|----------|----------------|
|
|
421
|
+
| **BlocFeed** | `blocfeed.com` |
|
|
422
|
+
| **Google Analytics / Ads** | `google-analytics.com`, `googletagmanager.com`, `analytics.google.com`, `googleads.g.doubleclick.net`, `googlesyndication.com`, `googleadservices.com`, `google.com/pagead`, `google.com/ads` |
|
|
423
|
+
| **Facebook / Meta** | `facebook.net`, `facebook.com/tr`, `connect.facebook.net`, `graph.facebook.com` |
|
|
424
|
+
| **Mixpanel** | `api.mixpanel.com`, `api-js.mixpanel.com` |
|
|
425
|
+
| **Amplitude** | `api.amplitude.com`, `api2.amplitude.com` |
|
|
426
|
+
| **Segment** | `api.segment.io`, `cdn.segment.com` |
|
|
427
|
+
| **Hotjar** | `vars.hotjar.com`, `in.hotjar.com`, `script.hotjar.com` |
|
|
428
|
+
| **Heap** | `heapanalytics.com` |
|
|
429
|
+
| **FullStory** | `fullstory.com/s/fs.js`, `rs.fullstory.com` |
|
|
430
|
+
| **PostHog** | `app.posthog.com`, `us.posthog.com`, `eu.posthog.com` |
|
|
431
|
+
| **Intercom** | `api-iam.intercom.io`, `widget.intercom.io` |
|
|
432
|
+
| **Sentry** | `sentry.io/api`, `browser.sentry-cdn.com` |
|
|
433
|
+
| **Datadog** | `browser-intake-datadoghq.com` |
|
|
434
|
+
| **Microsoft Clarity** | `clarity.ms` |
|
|
435
|
+
| **HubSpot** | `js.hs-analytics.net`, `api.hubapi.com`, `forms.hubspot.com` |
|
|
436
|
+
| **Plausible** | `plausible.io/api` |
|
|
437
|
+
| **Crisp** | `client.crisp.chat` |
|
|
438
|
+
| **Drift** | `js.driftt.com` |
|
|
439
|
+
| **LogRocket** | `r.logrocket.com` |
|
|
440
|
+
| **Pendo** | `app.pendo.io` |
|
|
441
|
+
| **LaunchDarkly** | `events.launchdarkly.com`, `app.launchdarkly.com` |
|
|
442
|
+
| **Grammarly** | `grammarly.com`, `gnar.grammarly.com`, `capi.grammarly.com` |
|
|
443
|
+
| **LanguageTool** | `api.languagetool.org`, `api.languagetoolplus.com` |
|
|
444
|
+
| **Google Fonts** | `fonts.googleapis.com`, `fonts.gstatic.com` |
|
|
445
|
+
| **Cookie consent** | `cdn.cookielaw.org`, `consent.cookiebot.com` |
|
|
446
|
+
| **Stripe.js** | `js.stripe.com`, `api.stripe.com/v1/tokens` |
|
|
447
|
+
| **CAPTCHA** | `google.com/recaptcha`, `hcaptcha.com` |
|
|
448
|
+
| **New Relic** | `bam.nr-data.net` |
|
|
449
|
+
| **Smartlook** | `rec.smartlook.com` |
|
|
450
|
+
| **Mouseflow** | `o2.mouseflow.com` |
|
|
451
|
+
| **Lucky Orange** | `api.luckyorange.com` |
|
|
452
|
+
| **Zendesk** | `static.zdassets.com`, `ekr.zdassets.com` |
|
|
453
|
+
|
|
454
|
+
Each pattern is matched as a **substring** against the request URL (case-insensitive).
|
|
455
|
+
|
|
456
|
+
#### Add extra domains
|
|
457
|
+
|
|
458
|
+
Pass additional patterns alongside the built-in blocklist:
|
|
459
|
+
|
|
460
|
+
```tsx
|
|
461
|
+
config={{
|
|
462
|
+
diagnostics: {
|
|
463
|
+
network: true,
|
|
464
|
+
ignoreUrls: ["my-internal-analytics.com", "custom-tracker.io"],
|
|
465
|
+
},
|
|
466
|
+
}}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
> **Note**: When you provide `ignoreUrls`, it **replaces** the built-in blocklist entirely. To keep the defaults and add your own patterns, you would need to include them manually — or simply omit `ignoreUrls` to use the built-in list as-is.
|
|
470
|
+
|
|
471
|
+
#### Capture everything (disable blocklist)
|
|
472
|
+
|
|
473
|
+
```tsx
|
|
474
|
+
config={{
|
|
475
|
+
diagnostics: {
|
|
476
|
+
network: true,
|
|
477
|
+
ignoreUrls: [], // empty array disables all filtering
|
|
478
|
+
},
|
|
479
|
+
}}
|
|
480
|
+
```
|
|
481
|
+
|
|
406
482
|
### Headless diagnostics
|
|
407
483
|
|
|
408
484
|
```ts
|
|
@@ -533,7 +609,7 @@ When a submission fails due to a network error, the payload is automatically sav
|
|
|
533
609
|
- On the next page load (1s after controller initialization)
|
|
534
610
|
- When the browser comes back online (`online` event)
|
|
535
611
|
|
|
536
|
-
Screenshots are stripped from queued payloads to stay within `localStorage` limits. The queue holds up to 50 items (FIFO eviction).
|
|
612
|
+
Screenshots and video recordings are stripped from queued payloads to stay within `localStorage` limits. The queue holds up to 50 items (FIFO eviction).
|
|
537
613
|
|
|
538
614
|
## Picking rules (ignore / filter)
|
|
539
615
|
|
|
@@ -584,6 +660,85 @@ Screenshots are uploaded efficiently using a two-phase flow:
|
|
|
584
660
|
|
|
585
661
|
Both **element** and **full-page** screenshots are supported and stored on the platform.
|
|
586
662
|
|
|
663
|
+
## Video Recording
|
|
664
|
+
|
|
665
|
+
Let users record a short screen capture clip alongside their feedback. Uses the browser's Screen Capture API (`getDisplayMedia`) to record the current tab as WebM video.
|
|
666
|
+
|
|
667
|
+
```tsx
|
|
668
|
+
<BlocFeedWidget
|
|
669
|
+
blocfeed_id="bf_..."
|
|
670
|
+
config={{
|
|
671
|
+
recording: {
|
|
672
|
+
enabled: true, // default: false
|
|
673
|
+
maxDurationMs: 30_000, // default: 30s — auto-stops at this limit
|
|
674
|
+
videoBitsPerSecond: 2_500_000, // default: 2.5 Mbps (~9 MB for 30s)
|
|
675
|
+
},
|
|
676
|
+
}}
|
|
677
|
+
/>
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### `RecordingConfig` options
|
|
681
|
+
|
|
682
|
+
| Option | Type | Default | Description |
|
|
683
|
+
|--------|------|---------|-------------|
|
|
684
|
+
| `enabled` | `boolean` | `false` | Show the Record button in the review phase |
|
|
685
|
+
| `maxDurationMs` | `number` | `30000` | Maximum recording duration in ms. Auto-stops when reached |
|
|
686
|
+
| `mime` | `"video/webm"` | `"video/webm"` | Output format (WebM is the only supported format) |
|
|
687
|
+
| `videoBitsPerSecond` | `number` | `2500000` | Video bitrate. Lower = smaller files, faster uploads |
|
|
688
|
+
|
|
689
|
+
### Bitrate & file size guide
|
|
690
|
+
|
|
691
|
+
| `videoBitsPerSecond` | 30s file size | Upload time (10 Mbps) |
|
|
692
|
+
|---|---|---|
|
|
693
|
+
| `1_000_000` (1 Mbps) | ~3.75 MB | ~3s |
|
|
694
|
+
| `2_500_000` (2.5 Mbps) | ~9.4 MB | ~7s |
|
|
695
|
+
| `5_000_000` (5 Mbps) | ~18.8 MB | ~15s |
|
|
696
|
+
|
|
697
|
+
Lower bitrates are fine for bug reproduction clips. 2.5 Mbps (default) gives crisp screen recordings.
|
|
698
|
+
|
|
699
|
+
### How it works
|
|
700
|
+
|
|
701
|
+
1. A **Record** button appears in the review phase (after element selection)
|
|
702
|
+
2. Clicking it triggers the browser's screen sharing permission prompt
|
|
703
|
+
3. A pulsing red dot + elapsed timer shows during recording
|
|
704
|
+
4. Recording stops when the user clicks **Stop**, the max duration is reached, or the browser's "Stop sharing" button is clicked
|
|
705
|
+
5. A video preview with playback controls appears — the user can remove it or submit
|
|
706
|
+
6. On submit, the video is uploaded directly to object storage via a presigned URL
|
|
707
|
+
|
|
708
|
+
### Browser support
|
|
709
|
+
|
|
710
|
+
Video recording requires `getDisplayMedia` and `MediaRecorder` with WebM support. The Record button is **automatically hidden** on unsupported browsers.
|
|
711
|
+
|
|
712
|
+
| Browser | Supported |
|
|
713
|
+
|---------|-----------|
|
|
714
|
+
| Chrome / Edge | Yes |
|
|
715
|
+
| Firefox | Yes |
|
|
716
|
+
| Safari 16+ | No (WebM not supported by MediaRecorder) |
|
|
717
|
+
|
|
718
|
+
### Programmatic API
|
|
719
|
+
|
|
720
|
+
```tsx
|
|
721
|
+
const ref = useRef<BlocFeedHandle>(null);
|
|
722
|
+
|
|
723
|
+
ref.current?.startRecording(); // start recording (must be in review phase)
|
|
724
|
+
ref.current?.stopRecording(); // stop recording
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### Localization
|
|
728
|
+
|
|
729
|
+
Override recording-related UI text via `config.ui.strings`:
|
|
730
|
+
|
|
731
|
+
```tsx
|
|
732
|
+
strings: {
|
|
733
|
+
recordButton: "Record",
|
|
734
|
+
stopRecordButton: "Stop",
|
|
735
|
+
recordingText: "Recording…",
|
|
736
|
+
recordingDeniedText: "Screen recording permission was denied.",
|
|
737
|
+
videoPreviewLabel: "Video preview",
|
|
738
|
+
removeVideoButton: "Remove video",
|
|
739
|
+
}
|
|
740
|
+
```
|
|
741
|
+
|
|
587
742
|
### Known limitations
|
|
588
743
|
|
|
589
744
|
The default screenshot engine (`html-to-image`) has known issues with:
|
|
@@ -693,6 +848,10 @@ import type {
|
|
|
693
848
|
TransportConfig,
|
|
694
849
|
TriggerStyle,
|
|
695
850
|
WidgetPosition,
|
|
851
|
+
RecordingConfig,
|
|
852
|
+
VideoAsset,
|
|
853
|
+
VideoIntent,
|
|
854
|
+
VideoMime,
|
|
696
855
|
SecurityConfig,
|
|
697
856
|
SecurityFinding,
|
|
698
857
|
SecuritySnapshot,
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function E(){return typeof window<"u"&&typeof document<"u"}function he(e){let t=globalThis.CSS;return typeof t?.escape=="function"?t.escape(e):e.replace(/[^a-zA-Z0-9_-]/g,n=>{let r=n.codePointAt(0);return r===void 0?"":`\\${r.toString(16)} `})}function te(e){return {x:e.x,y:e.y,width:e.width,height:e.height}}function $e(e){return {x:e.x+window.scrollX,y:e.y+window.scrollY,width:e.width,height:e.height}}function je(e){return e.replace(/\s+/g," ").trim()}function Ve(e,t=140){let n=e.textContent;if(!n)return;let r=je(n);if(r)return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}function Xe(e){let t=1;for(let n=e.previousElementSibling;n;n=n.previousElementSibling)n.tagName===e.tagName&&(t+=1);return t}var Ee=["data-testid","data-test-id","data-test","data-qa","data-cy"],we="data-blocfeed-component";function Ze(e){let t=e.closest(`[${we}]`);if(!t)return;let r=t.getAttribute(we)?.trim();return r||void 0}function We(e){for(let t of Ee){let n=e.closest(`[${t}]`);if(!n)continue;let o=n.getAttribute(t)?.trim();if(o)return o}}function be(e){try{let t=e,n=Object.getOwnPropertyNames(t);for(let r of n)if(r.startsWith("__reactFiber$")||r.startsWith("__reactInternalInstance$")){let o=t[r];if(o&&typeof o=="object")return o}}catch{}return null}function B(e){if(e.length<=1||e.length===2&&e[0]===e[0].toLowerCase())return true;let t=e[0];return t>="a"&&t<="z"}function q(e){if(e){for(let t of e)if(typeof t.name=="string"&&t.name&&!B(t.name))return t.name}}function A(e){if(e&&typeof e!="string"){if(typeof e=="function"){let t=e;return typeof t.displayName=="string"&&t.displayName?t.displayName:typeof t.name=="string"&&t.name?t.name:void 0}if(typeof e=="object"){let t=e,n=t.displayName;if(typeof n=="string"&&n)return n;let r=t.render;if(typeof r=="function"){let i=r;if(typeof i.displayName=="string"&&i.displayName)return i.displayName;if(typeof i.name=="string"&&i.name)return i.name}let o=t.type;return A(o)}}}function Ke(e){let t=be(e);if(!t)return;let n=q(t._debugInfo);if(n)return n;let r=t._debugOwner!==void 0;if(r){let s=t._debugOwner;for(let c=0;s&&c<50;c+=1){let h=q(s._debugInfo);if(h)return h;let p=A(s.type)??A(s.elementType);if(p&&!B(p))return p;s=s._debugOwner;}}if(t._owner!==void 0&&t._owner!==t._debugOwner){let s=t._owner;for(let c=0;s&&c<50;c+=1){let h=q(s._debugInfo);if(h)return h;let p=A(s.type)??A(s.elementType);if(p&&!B(p))return p;s=s._owner;}}let i=t,l=r?80:25;for(let s=0;i&&s<l;s+=1){let c=q(i._debugInfo);if(c)return c;let h=A(i.type)??A(i.elementType);if(h&&!B(h))return h;i=i.return;}let a=e.parentElement;for(let s=0;a&&s<15;s+=1){let c=be(a);if(c){let h=q(c._debugInfo);if(h)return h;let p=A(c.type)??A(c.elementType);if(p&&!B(p))return p;if(c._debugOwner){let m=A(c._debugOwner.type)??A(c._debugOwner.elementType);if(m&&!B(m))return m}if(c._owner&&c._owner!==c._debugOwner){let m=A(c._owner.type)??A(c._owner.elementType);if(m&&!B(m))return m}}a=a.parentElement;}}function Qe(e){let t=e.tagName.toLowerCase(),n=e.getAttribute("id");if(n)return `#${he(n)}`;for(let r of Ee){let o=e.getAttribute(r);if(o)return `${t}[${r}="${he(o)}"]`}return `${t}:nth-of-type(${Xe(e)})`}function Ye(e,t=10){let n=[],r=e;for(;r&&n.length<t;){let o=Qe(r);if(n.unshift(o),o.startsWith("#"))break;r=r.parentElement;}return n.join(" > ")}function ye(e,t){if(!t||t.length===0)return false;for(let n of t)if(e.closest(n))return true;return false}function ne(e,t){if(!e||ye(e,t.ignoreSelectors))return null;let n=e;for(;n;){if(ye(n,t.ignoreSelectors))return null;if(t.isSelectable?.(n)??Ge(n))return n;n=n.parentElement;}return null}function Ge(e){let t=e.tagName;return !(t==="HTML"||t==="BODY")}function Se(e){let t=e.getBoundingClientRect(),n={selector:Ye(e),tagName:e.tagName.toLowerCase(),rect:te(t),pageRect:$e(t)},r=e.getAttribute("id");r&&(n.id=r);let o=e.className;typeof o=="string"&&o.trim()&&(n.className=o);let i=Ve(e);i&&(n.textSnippet=i);let l=Ze(e)??Ke(e);l&&(n.componentName=l);let a=We(e);return a&&(n.testId=a),n}var re=null;async function ke(){return re||(re=import('html-to-image')),re}async function Je(e){return await new Promise((t,n)=>{let r=new Image;r.onload=()=>t({width:r.naturalWidth,height:r.naturalHeight}),r.onerror=()=>n(new Error("Failed to load generated screenshot")),r.src=e;})}async function _e(e,t){let{width:n,height:r}=await Je(e);return {dataUrl:e,mime:t,width:n,height:r}}function O(e){if(e?.aborted)throw new Error("Aborted")}function ve(){return {async captureElement(e,t){if(!E())throw new Error("captureElement can only run in the browser");O(t.signal);let n=await ke();O(t.signal);let r=t.mime==="image/jpeg"?await n.toJpeg(e,{quality:t.quality??.92,pixelRatio:t.pixelRatio}):await n.toPng(e,{pixelRatio:t.pixelRatio});return O(t.signal),await _e(r,t.mime)},async captureFullPage(e){if(!E())throw new Error("captureFullPage can only run in the browser");O(e.signal);let t=document.documentElement,n=Math.max(t.scrollWidth,t.clientWidth),r=Math.max(t.scrollHeight,t.clientHeight),o=Math.min(1,e.maxDimension/Math.max(n,r)),i=Math.max(1,Math.round(n*o)),l=Math.max(1,Math.round(r*o)),a=await ke();O(e.signal);let s=e.mime==="image/jpeg"?await a.toJpeg(t,{width:i,height:l,quality:e.quality??.92,pixelRatio:e.pixelRatio}):await a.toPng(t,{width:i,height:l,pixelRatio:e.pixelRatio});return O(e.signal),await _e(s,e.mime)}}}function et(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return "Unknown error"}}function w(e,t,n){let r={kind:e,message:et(t)};return n&&(r.detail=n),r}var tt=12e3,nt=2048,rt=.92;function Re(){return Date.now()}function ot(e){return new Promise((t,n)=>{let r=()=>n(new Error("Aborted"));if(e.aborted){r();return}e.addEventListener("abort",r,{once:true});})}async function Ae(e,t,n){let r=new Promise((i,l)=>{let a=setTimeout(()=>l(new Error("Timeout")),t);typeof a.unref=="function"&&a.unref();}),o=[e,r];return n&&o.push(ot(n)),await Promise.race(o)}function it(e){if(!E())return 1;let t=window.devicePixelRatio||1,n=e?.pixelRatio??Math.min(t,2);return Math.max(.1,n)}function at(e){return !!(e?.element||e?.fullPage)}function Te(e){let t={mime:e.mime,pixelRatio:e.pixelRatio,maxDimension:e.maxDimension};return e.includeQuality&&(t.quality=e.quality),e.signal&&(t.signal=e.signal),t}async function Pe(e){let{selectionElement:t,capture:n,signal:r}=e;if(!E()||!at(n))return;let o=Re(),i=[],l=n?.timeoutMs??tt,a=n?.maxDimension??nt,s=n?.mime??"image/png",c=n?.quality??rt,h=n?.adapter??ve(),p={},m=it(n);if(n?.element&&t)try{let f=t.getBoundingClientRect(),g=Math.min(1,a/Math.max(f.width,f.height)),k=Math.min(m,m*g),v=await Ae(Promise.resolve(h.captureElement(t,{...Te({mime:s,quality:c,pixelRatio:k,maxDimension:a,includeQuality:s==="image/jpeg",...r?{signal:r}:{}})})),l,r);p.element=v;}catch(f){if(r?.aborted)throw f;i.push(w("capture_failed",f,{target:"element"}));}if(n?.fullPage)try{let f=await Ae(Promise.resolve(h.captureFullPage(Te({mime:s,quality:c,pixelRatio:m,maxDimension:a,includeQuality:s==="image/jpeg",...r?{signal:r}:{}}))),l,r);p.fullPage=f;}catch(f){if(r?.aborted)throw f;i.push(w("capture_failed",f,{target:"fullPage"}));}let b=Re(),u={startedAt:o,finishedAt:b,durationMs:Math.max(0,b-o)};return i.length>0&&(u.errors=i),{...p,diagnostics:u}}function st(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}function ct(){return E()?{url:window.location.href,title:document.title,referrer:document.referrer||void 0,userAgent:navigator.userAgent,language:navigator.language,platform:navigator.platform,viewport:{width:window.innerWidth,height:window.innerHeight},screen:{width:window.screen.width,height:window.screen.height},scroll:{x:window.scrollX,y:window.scrollY},devicePixelRatio:window.devicePixelRatio||1,timezone:st()}:{}}function lt(e){if(!e)return {};let t={};return e.id&&(t.userId=e.id),e.email&&(t.userEmail=e.email),e.name&&(t.userName=e.name),t}async function xe(e){let{config:t,context:n,user:r}=e;if(t?.enabled===false)return {};let o={...ct(),...lt(r)},i=t?.enrich;if(!i)return o;try{let l=await i(n);return {...o,...l}}catch(l){let a=w("unknown",l);return {...o,blocfeedMetadataError:a.message}}}var oe="blocfeed-queue",ut=50;function ie(){if(!E())return [];try{let e=localStorage.getItem(oe);if(!e)return [];let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return []}}function ae(e){if(E())try{e.length===0?localStorage.removeItem(oe):localStorage.setItem(oe,JSON.stringify(e));}catch{}}function dt(e){let t={...e};if(t.screenshots){let n={...t.screenshots};n.element&&(n.element={...n.element,dataUrl:""}),n.fullPage&&(n.fullPage={...n.fullPage,dataUrl:""}),t.screenshots=n;}return delete t.video,t}function se(e){let t=ie(),n=dt(e);for(n.metadata={...n.metadata,_queued:true},t.push({payload:n,timestamp:Date.now()});t.length>ut;)t.shift();ae(t);}function Fe(){let e=ie();return e.length===0?[]:(ae([]),e.map(t=>t.payload))}function Jt(){ae([]);}function en(){return ie().length}var ft=3e4,mt=25e5,Ce="video/webm",pt=250,gt=1e3,ht=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"];function Me(){if(typeof MediaRecorder>"u")return null;for(let e of ht)if(MediaRecorder.isTypeSupported(e))return e;return null}function wt(){return !E()||typeof navigator?.mediaDevices?.getDisplayMedia!="function"?false:Me()!==null}async function ce(e){let{config:t,signal:n}=e;if(!E()||typeof navigator?.mediaDevices?.getDisplayMedia!="function")throw w("recording_failed",new Error("Screen recording is not supported in this browser"));let r=Me();if(!r)throw w("recording_failed",new Error("No supported video codec found (WebM required)"));if(n?.aborted)throw w("aborted",new Error("Recording aborted before start"));let o;try{o=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1});}catch(g){let k=g instanceof DOMException&&g.name==="NotAllowedError"?"Screen share permission denied":"Failed to start screen share";throw w("recording_failed",new Error(k))}if(n?.aborted)throw o.getTracks().forEach(g=>g.stop()),w("aborted",new Error("Recording aborted after permission"));let i=t?.maxDurationMs??ft,l=t?.videoBitsPerSecond??mt,a=new MediaRecorder(o,{mimeType:r,videoBitsPerSecond:l}),s=[],c=[],h=0,p=null,m=null,b=false,u=()=>{p!==null&&(clearInterval(p),p=null),m!==null&&(clearTimeout(m),m=null),o.getTracks().forEach(g=>g.stop());},f=new Promise((g,k)=>{a.ondataavailable=y=>{y.data.size>0&&s.push(y.data);},a.onstop=()=>{if(b)return;b=true,u();let y=Date.now()-h,P=new Blob(s,{type:Ce}),M=URL.createObjectURL(P);g({mime:Ce,blobUrl:M,blob:P,durationMs:y,sizeBytes:P.size});},a.onerror=()=>{b||(b=true,u(),k(w("recording_failed",new Error("MediaRecorder error"))));};let v=o.getVideoTracks()[0];if(v&&v.addEventListener("ended",()=>{a.state!=="inactive"&&a.stop();}),n){let y=()=>{b||(b=true,a.state!=="inactive"&&a.stop(),u(),k(w("aborted",new Error("Recording aborted"))));};n.addEventListener("abort",y,{once:true});}});return a.start(gt),h=Date.now(),p=setInterval(()=>{let g=Date.now()-h;for(let k of c)k(g);},pt),m=setTimeout(()=>{!b&&a.state!=="inactive"&&a.stop();},i),{result:f,stop(){!b&&a.state!=="inactive"&&a.stop();},onTick(g){c.push(g);},abort(){b||(b=true,a.state!=="inactive"&&a.stop(),u());}}}function le(e){let t=null,n=null,r=(...o)=>{n=o,t===null&&(t=requestAnimationFrame(()=>{if(t=null,!n)return;let i=n;n=null,e(...i);}));};return r.cancel=()=>{t!==null&&cancelAnimationFrame(t),t=null,n=null;},r}function K(e){return e instanceof Element?!!e.closest("[data-blocfeed-ui]"):false}function Q(e){e.stopPropagation(),e.stopImmediatePropagation?.();}function Le(e,t){if(!E())throw new Error("BlocFeed picker can only run in a browser environment.");let n=e.ignoreSelectors,r=e.isSelectable,o={};n&&n.length>0&&(o.ignoreSelectors=n),r&&(o.isSelectable=r);let i=null,l=null,a=(u,f=false)=>{if(!u){i=null,l=null,t.onHover(null);return}let g=te(u.getBoundingClientRect()),k=`${Math.round(g.x)}:${Math.round(g.y)}:${Math.round(g.width)}:${Math.round(g.height)}`;!f&&u===i&&k===l||(i=u,l=k,t.onHover({element:u,rect:g}));},s=le(u=>{if(K(u.target))return;let f=document.elementFromPoint(u.clientX,u.clientY),g=ne(f,o);a(g);}),c=le(()=>{i&&a(i,true);}),h=u=>{K(u.target)||(Q(u),u.pointerType==="mouse"&&u.preventDefault());},p=u=>{K(u.target)||(Q(u),u.pointerType==="mouse"&&u.preventDefault());},m=u=>{if(K(u.target))return;Q(u),u.preventDefault();let f=document.elementFromPoint(u.clientX,u.clientY),g=ne(f,o);g&&t.onSelect({element:g,descriptor:Se(g)});},b=u=>{u.key==="Escape"&&(Q(u),u.preventDefault(),t.onCancel());};return window.addEventListener("pointermove",s,{capture:true,passive:true}),window.addEventListener("pointerdown",h,{capture:true}),window.addEventListener("pointerup",p,{capture:true}),window.addEventListener("click",m,{capture:true}),window.addEventListener("keydown",b,{capture:true}),window.addEventListener("scroll",c,{capture:true,passive:true}),window.addEventListener("resize",c,{passive:true}),{stop(){window.removeEventListener("pointermove",s,{capture:true}),window.removeEventListener("pointerdown",h,{capture:true}),window.removeEventListener("pointerup",p,{capture:true}),window.removeEventListener("click",m,{capture:true}),window.removeEventListener("keydown",b,{capture:true}),window.removeEventListener("scroll",c,{capture:true}),window.removeEventListener("resize",c),s.cancel(),c.cancel(),t.onHover(null);}}}var bt=12e3,yt=2,Et=500,St=2e3,ue="https://blocfeed.com/api/feedback",De=0;function Be(e,t){return new Promise((n,r)=>{if(t?.aborted){r(new Error("Aborted"));return}let o=setTimeout(n,e),i=()=>{clearTimeout(o),r(new Error("Aborted"));};t?.addEventListener("abort",i,{once:true});})}function kt(e){return e>=500&&e<=599}function _t(e){let[t,n]=e.split(",",2);if(!t||!n)throw new Error("Invalid data URL");let o=/data:(.*?);base64/.exec(t)?.[1]||"application/octet-stream",i=atob(n),l=new Uint8Array(i.length);for(let a=0;a<i.length;a+=1)l[a]=i.charCodeAt(a);return new Blob([l],{type:o})}function vt(e){let t={},n={},r={...e};if(r.screenshots){let o={},i={...r.screenshots};i.element&&(t.element=i.element.dataUrl,o.element={mime:i.element.mime,width:i.element.width,height:i.element.height},i.element={...i.element,dataUrl:""}),i.fullPage&&(t.fullPage=i.fullPage.dataUrl,o.fullPage={mime:i.fullPage.mime,width:i.fullPage.width,height:i.fullPage.height},i.fullPage={...i.fullPage,dataUrl:""}),r.screenshots=i,(o.element||o.fullPage)&&(r.screenshot_intent=o);}return r.video&&(n.blob=r.video.blob,r.video_intent={mime:r.video.mime,durationMs:r.video.durationMs,sizeBytes:r.video.sizeBytes},delete r.video),{lean:r,extracted:t,extractedVideo:n}}async function Ie(e,t,n){let r=_t(t);await fetch(e,{method:"PUT",body:r,headers:{"content-type":r.type},...n?{signal:n}:{}});}async function Rt(e){let{feedbackId:t,extracted:n,screenshots:r,signal:o}=e,i={};n.element&&r?.element&&(i.element={dataUrl:n.element,mime:r.element.mime,width:r.element.width,height:r.element.height}),n.fullPage&&r?.fullPage&&(i.fullPage={dataUrl:n.fullPage,mime:r.fullPage.mime,width:r.fullPage.width,height:r.fullPage.height}),Object.keys(i).length!==0&&await fetch(`${ue}/${t}/screenshots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(i),...o?{signal:o}:{}});}async function At(e){await fetch(`${ue}/${e.feedbackId}/video`,{method:"POST",body:e.blob,headers:{"content-type":e.blob.type},...e.signal?{signal:e.signal}:{}});}async function Ne(e){let{signal:t,transport:n}=e;if(Date.now()-De<St)return {ok:false,error:w("configuration",new Error("Please wait before submitting again"))};let o=n?.timeoutMs??bt,i=n?.maxAttempts??yt,l=n?.backoffMs??Et,a=!!(e.payload.screenshots?.element?.dataUrl||e.payload.screenshots?.fullPage?.dataUrl),s=!!e.payload.video?.blob,c=a||s,{lean:h,extracted:p,extractedVideo:m}=c?vt(e.payload):{lean:e.payload,extracted:{},extractedVideo:{}},b={...p,...e.screenshotDataUrls};for(let u=1;u<=i;u+=1){let f=new AbortController,g=setTimeout(()=>f.abort(),o),k=()=>f.abort();t&&t.addEventListener("abort",k,{once:true});try{let v=await fetch(ue,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(h),signal:f.signal});if(v.ok){De=Date.now();let y;try{y=await v.json();}catch{}if((b.element||b.fullPage)&&y){let d=y.upload_urls;if(d){let S=[];b.element&&d.element&&S.push(Ie(d.element,b.element,t)),b.fullPage&&d.fullPage&&S.push(Ie(d.fullPage,b.fullPage,t));try{await Promise.all(S);}catch{}}else if(y.feedback_id)try{await Rt({feedbackId:y.feedback_id,extracted:b,screenshots:e.payload.screenshots,...t?{signal:t}:{}});}catch{}}if(m.blob&&y){let d=y.upload_urls;if(d?.video)try{await fetch(d.video,{method:"PUT",body:m.blob,headers:{"content-type":m.blob.type},...t?{signal:t}:{}});}catch{}else if(y.feedback_id)try{await At({feedbackId:y.feedback_id,blob:m.blob,...t?{signal:t}:{}});}catch{}}let M={ok:!0,status:v.status};return y&&(M.apiResponse=y),M}if(u<i&&kt(v.status)){let y=.85+Math.random()*.3,P=Math.round(l*2**(u-1)*y);await Be(P,t);continue}return {ok:!1,status:v.status,error:w("api_failed",new Error(`HTTP ${v.status}`))}}catch(v){if(f.signal.aborted||t?.aborted)return {ok:false,error:w("aborted",v)};if(u<i){let y=.85+Math.random()*.3,P=Math.round(l*2**(u-1)*y);await Be(P,t);continue}return {ok:false,error:w("api_failed",v)}}finally{clearTimeout(g),t&&t.removeEventListener("abort",k);}}return {ok:false,error:w("api_failed",new Error("Failed"))}}async function de(e){let{signal:t,transport:n}=e,r={ok:false};try{let o={payload:e.payload,...t?{signal:t}:{},...n?{transport:n}:{}};r.api=await Ne(o),r.ok=!!r.api?.ok;}catch(o){r.api={ok:false,error:w("api_failed",o)},r.ok=false;}return {payload:e.payload,result:r}}var Tt=["[data-blocfeed-ui]","[data-blocfeed-ignore]"];function Pt(e){let t=[...Tt,...e?.ignoreSelectors??[]],n=Array.from(new Set(t));return {...e,ignoreSelectors:n}}function Oe(){return {phase:"idle"}}function xt(e){if(e.ok)return false;let t=e.api;return t?t.status&&t.status>=400&&t.status<500||t.error?.kind==="aborted"||t.error?.kind==="configuration"?false:!t.ok:true}function Rn(e){let t=e,n=Oe(),r=new Set,o=new Set,i=null,l=null,a=null,s=null,c=0,h=null,p=null,m=null,b=()=>{for(let d of r)d(n);},u=d=>{for(let S of o)S(d);},f=d=>{n=d,b();},g=()=>{c+=1,s?.abort(),s=null;},k=()=>{i?.stop(),i=null,u(null),a!==null&&E()&&(document.documentElement.style.cursor=a,a=null);},v=()=>{p&&(p.abort(),p=null),m&&(URL.revokeObjectURL(m.blobUrl),m=null);},y=()=>{g(),k(),v(),l=null,f(Oe());},P=()=>{if(!E())return;k(),l=null;let d=Pt(t.picker);a=document.documentElement.style.cursor,document.documentElement.style.cursor="crosshair",f({phase:"picking"}),i=Le(d,{onHover:u,onSelect:({element:S,descriptor:x})=>{l=S,k(),f({phase:"review",selection:x});},onCancel:()=>{y();}});},M=()=>{let d=Fe();if(d.length!==0)for(let S of d)de({payload:S,...t.transport?{transport:t.transport}:{}}).catch(()=>{se(S);});};if(E()){setTimeout(M,1e3);let d=()=>M();window.addEventListener("online",d),h=()=>window.removeEventListener("online",d);}return {getState:()=>n,subscribe(d){return r.add(d),()=>r.delete(d)},subscribeHover(d){return o.add(d),()=>o.delete(d)},start(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||n.phase!=="picking"&&P();},stop(){y();},clearSelection(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||P();},setConfig(d){t=d;},async submit(d,S){if(!E()){let R=w("configuration",new Error("BlocFeed submit can only run in the browser"));return f({phase:"error",lastError:R}),{ok:false}}let x=t.blocfeed_id?.trim?.()??"";if(!x){let L={phase:"error",lastError:w("configuration",new Error("Missing blocfeed_id. Create a project in BlocFeed and pass its blocfeed_id."))};return n.selection&&(L.selection=n.selection),f(L),{ok:false}}if(n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording")return {ok:false};let F=c+1;c=F,s?.abort(),s=new AbortController;let T=s.signal,_=n.selection,U=S?.capture?{...t.capture,...S.capture}:t.capture,H=!!(U?.element||U?.fullPage),ge={phase:H?"capturing":"submitting"};_&&(ge.selection=_),f(ge);try{let R=H?await Pe({selectionElement:l,capture:U,signal:T}):void 0;if(T.aborted||c!==F)return {ok:!1};let L={phase:"submitting"};_&&(L.selection=_),R&&(L.capture=R),f(L);let N={};_&&(N.selection=_),R&&(N.capture=R);let qe=await xe({config:t.metadata,context:N,...t.user?{user:t.user}:{}}),D={version:1,createdAt:new Date().toISOString(),blocfeed_id:x,message:d,metadata:qe};S?.category&&(D.category=S.category),t.user&&(D.user=t.user),_&&(D.selection=_),R&&(D.screenshots=R),m&&(D.video=m);let{result:C}=await de({payload:D,signal:T,...t.transport?{transport:t.transport}:{}});if(T.aborted||c!==F)return C;if(C.ok){m&&(URL.revokeObjectURL(m.blobUrl),m=null);let ee={phase:"success",lastSubmit:C};return _&&(ee.selection=_),R&&(ee.capture=R),f(ee),C}xt(C)&&se(D);let ze=C.api?.error??w("unknown",new Error("Submission failed")),J={phase:"error",lastSubmit:C,lastError:ze};return _&&(J.selection=_),R&&(J.capture=R),f(J),C}catch(R){if(T.aborted||c!==F)return {ok:false};let N={phase:"error",lastError:T.aborted?w("aborted",R):w("unknown",R)};return _&&(N.selection=_),f(N),{ok:false}}finally{c===F&&(s=null);}},async startRecording(){if(n.phase!=="review"||!t.recording?.enabled)return;let d=n.selection,S={phase:"recording",recordingElapsedMs:0};d&&(S.selection=d),f(S);try{let x={config:t.recording};s&&(x.signal=s.signal);let F=await ce(x);p=F,F.onTick(U=>{if(n.phase==="recording"){let H={phase:"recording",recordingElapsedMs:U};d&&(H.selection=d),f(H);}});let T=await F.result;p=null,m=T;let _={phase:"review",video:T};d&&(_.selection=d),f(_);}catch(x){p=null;let T={phase:"review",lastError:x?.kind?x:w("recording_failed",x)};d&&(T.selection=d),f(T);}},stopRecording(){n.phase!=="recording"||!p||p.stop();},removeVideo(){m&&(URL.revokeObjectURL(m.blobUrl),m=null);let d=n.selection,S={phase:"review"};d&&(S.selection=d),f(S);},__unsafeGetSelectedElement(){return l},destroy(){y(),r.clear(),o.clear(),h?.(),h=null;}}}var z=[],$=[],Ue=20,He=15,Y=[],j={},V=null,X=null,Z=null,G=false,Ft=["blocfeed.com","google-analytics.com","googletagmanager.com","analytics.google.com","googleads.g.doubleclick.net","googlesyndication.com","googleadservices.com","google.com/pagead","google.com/ads","facebook.net","facebook.com/tr","connect.facebook.net","graph.facebook.com","api.mixpanel.com","api-js.mixpanel.com","api.amplitude.com","api2.amplitude.com","api.segment.io","cdn.segment.com","vars.hotjar.com","in.hotjar.com","script.hotjar.com","heapanalytics.com","fullstory.com/s/fs.js","rs.fullstory.com","app.posthog.com","us.posthog.com","eu.posthog.com","api-iam.intercom.io","widget.intercom.io","sentry.io/api","browser.sentry-cdn.com","browser-intake-datadoghq.com","clarity.ms","js.hs-analytics.net","api.hubapi.com","forms.hubspot.com","plausible.io/api","client.crisp.chat","js.driftt.com","r.logrocket.com","app.pendo.io","events.launchdarkly.com","app.launchdarkly.com","grammarly.com","gnar.grammarly.com","capi.grammarly.com","api.languagetool.org","api.languagetoolplus.com","fonts.googleapis.com","fonts.gstatic.com","cdn.cookielaw.org","consent.cookiebot.com","js.stripe.com","api.stripe.com/v1/tokens","google.com/recaptcha","hcaptcha.com","bam.nr-data.net","rec.smartlook.com","o2.mouseflow.com","api.luckyorange.com","static.zdassets.com","ekr.zdassets.com"];function Ct(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function Mt(e,t){let n=t.map(Ct).join(" "),r=t.find(i=>i instanceof Error),o={level:e,message:n.slice(0,2e3),timestamp:Date.now()};for(r?.stack&&(o.stack=r.stack.slice(0,2e3)),z.push(o);z.length>Ue;)z.shift();}function fe(e){let t=e.url.toLowerCase();for(let n of Y)if(t.includes(n))return;for($.push(e);$.length>He;)$.shift();}function Pn(e={}){if(G||!E())return;G=true,Ue=e.consoleLimit??20,He=e.networkLimit??15;let t=e.ignoreUrls;if(t!==void 0?Y=t.map(n=>n.toLowerCase()):Y=[...Ft],e.console!==false){let n=e.consoleLevels??["error","warn"];for(let r of n)j[r]=console[r],console[r]=(...o)=>{Mt(r,o),j[r]?.apply(console,o);};}if(e.network!==false&&typeof window.fetch=="function"){V=window.fetch;let n=V;window.fetch=async function(o,i){let l=typeof o=="string"?o:o instanceof URL?o.toString():o.url,a=(i?.method??"GET").toUpperCase(),s=Date.now();try{let c=await n.call(window,o,i);return c.ok||fe({url:l.slice(0,500),method:a,status:c.status,statusText:c.statusText,timestamp:s,durationMs:Date.now()-s}),c}catch(c){throw fe({url:l.slice(0,500),method:a,status:0,statusText:c instanceof Error?c.message:"Network error",timestamp:s,durationMs:Date.now()-s}),c}};}if(e.network!==false&&typeof XMLHttpRequest<"u"){X=XMLHttpRequest.prototype.open,Z=XMLHttpRequest.prototype.send;let n=X,r=Z;XMLHttpRequest.prototype.open=function(o,i,...l){return this.__bf_method=o.toUpperCase(),this.__bf_url=String(i),n.apply(this,[o,i,...l])},XMLHttpRequest.prototype.send=function(...o){let i=this.__bf_method||"GET",l=this.__bf_url||"",a=Date.now();return this.addEventListener("loadend",function(){if(this.status>=400||this.status===0){let s={url:l.slice(0,500),method:i,status:this.status,timestamp:a,durationMs:Date.now()-a};this.statusText&&(s.statusText=this.statusText),fe(s);}}),r.apply(this,o)};}}function xn(){if(G){for(let[e,t]of Object.entries(j))console[e]=t;for(let e of Object.keys(j))delete j[e];V&&(window.fetch=V,V=null),X&&(XMLHttpRequest.prototype.open=X,X=null),Z&&(XMLHttpRequest.prototype.send=Z,Z=null),Y=[],G=false;}}function Fn(){return {consoleLogs:[...z],networkErrors:[...$]}}function Cn(){z=[],$=[];}var Lt=[{name:"supabase_service_role_key",pattern:/SUPABASE_SERVICE_ROLE_KEY["'=:\s]+[A-Za-z0-9_.=-]{30,}/},{name:"supabase_db_password",pattern:/SUPABASE_DB_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"postgres_password",pattern:/POSTGRES_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"database_url_with_creds",pattern:/(?:postgres|postgresql|mysql|mongodb|redis|amqp):\/\/[^:]+:[^@\s"']+@/},{name:"aws_access_key",pattern:/AKIA[0-9A-Z]{16}/},{name:"aws_secret_key",pattern:/AWS_SECRET_ACCESS_KEY["'=:\s]+[A-Za-z0-9/+=]{30,}/},{name:"aws_session_token",pattern:/AWS_SESSION_TOKEN["'=:\s]+[A-Za-z0-9/+=]{30,}/},{name:"stripe_secret_key",pattern:/sk_live_[a-zA-Z0-9]{10,}/},{name:"stripe_secret_key_test",pattern:/sk_test_[a-zA-Z0-9]{10,}/},{name:"stripe_restricted_key",pattern:/rk_(?:live|test)_[a-zA-Z0-9]{10,}/},{name:"github_pat",pattern:/ghp_[A-Za-z0-9_]{36,}/},{name:"github_oauth",pattern:/gho_[A-Za-z0-9_]{36,}/},{name:"github_user_token",pattern:/ghu_[A-Za-z0-9_]{36,}/},{name:"github_server_token",pattern:/ghs_[A-Za-z0-9_]{36,}/},{name:"github_fine_grained",pattern:/github_pat_[A-Za-z0-9_]{22,}/},{name:"openai_api_key",pattern:/sk-[a-zA-Z0-9]{20,}(?![a-zA-Z0-9_])/},{name:"anthropic_api_key",pattern:/sk-ant-[a-zA-Z0-9\-_]{20,}/},{name:"private_key",pattern:/-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----/},{name:"sendgrid_api_key",pattern:/SG\.[a-zA-Z0-9_-]{22,}\.[a-zA-Z0-9_-]{22,}/},{name:"twilio_auth_token",pattern:/TWILIO_AUTH_TOKEN["'=:\s]+[a-f0-9]{32}/},{name:"generic_secret_key",pattern:/[A-Z_]{2,}_SECRET_KEY["'=:\s]+[^\s"']{8,}/},{name:"generic_secret",pattern:/[A-Z_]{2,}_SECRET["'=:\s]+[^\s"']{8,}/},{name:"generic_password",pattern:/[A-Z_]{2,}_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"generic_private_key_var",pattern:/[A-Z_]{2,}_PRIVATE_KEY["'=:\s]+[^\s"']{8,}/}],I=[],pe=0,me=false;function Dt(e){let t=e.slice(0,80);return t.length<=6?"***":t.slice(0,6)+"***"}function Bt(e,t,n,r){if(I.some(l=>l.rule===e&&l.source===t))return;let i={rule:e,source:t,hint:Dt(n),timestamp:Date.now()};r&&(i.location=r),I.push(i);}function W(e,t,n,r){for(let{name:o,pattern:i}of n){let l=i.exec(e);l&&Bt(o,t,l[0],r);}}function It(e){try{let t=window;if(t.__NEXT_DATA__){let n=JSON.stringify(t.__NEXT_DATA__);W(n,"hydration",e,"__NEXT_DATA__");}if(t.__NUXT__){let n=JSON.stringify(t.__NUXT__);W(n,"hydration",e,"__NUXT__");}}catch{}}function Nt(e){try{document.querySelectorAll("script:not([src])").forEach((n,r)=>{let o=n.textContent;o&&o.length>0&&W(o,"scripts",e,`inline-script[${r}]`);});}catch{}}function Ot(e){try{document.querySelectorAll("meta[content]").forEach(n=>{let r=n.getAttribute("content"),o=n.getAttribute("name")||n.getAttribute("property")||"";r&&r.length>10&&W(r,"meta",e,o?`meta[${o}]`:void 0);});}catch{}}function Ut(e){try{document.querySelectorAll("[data-api-key], [data-secret], [data-token], [data-password]").forEach(n=>{let r=n.attributes;for(let o=0;o<r.length;o++){let i=r[o];i.name.startsWith("data-")&&i.value.length>10&&W(i.value,"dom",e,`${n.tagName.toLowerCase()}[${i.name}]`);}});}catch{}}function Dn(e={}){if(me||!E()||(me=true,e.secretScan===false))return;let t=[...Lt,...e.customPatterns??[]],n=e.scanTargets??["hydration","scripts","meta","dom"];setTimeout(()=>{pe=Date.now(),n.includes("hydration")&&It(t),n.includes("scripts")&&Nt(t),n.includes("meta")&&Ot(t),n.includes("dom")&&Ut(t);let r=e.notify??"both";I.length>0&&(r==="console"||r==="both")&&console.warn(`[BlocFeed Security] Found ${I.length} potential secret(s) exposed in client code:`,I.map(o=>`${o.rule} (${o.source}${o.location?`: ${o.location}`:""})`));},100);}function Bn(){return {findings:[...I],scannedAt:pe}}function In(){I=[],pe=0,me=false;}
|
|
2
|
+
export{E as a,te as b,ve as c,Pe as d,xe as e,se as f,Fe as g,Jt as h,en as i,wt as j,ce as k,Rn as l,Pn as m,xn as n,Fn as o,Cn as p,Dn as q,Bn as r,In as s};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';function E(){return typeof window<"u"&&typeof document<"u"}function he(e){let t=globalThis.CSS;return typeof t?.escape=="function"?t.escape(e):e.replace(/[^a-zA-Z0-9_-]/g,n=>{let r=n.codePointAt(0);return r===void 0?"":`\\${r.toString(16)} `})}function te(e){return {x:e.x,y:e.y,width:e.width,height:e.height}}function $e(e){return {x:e.x+window.scrollX,y:e.y+window.scrollY,width:e.width,height:e.height}}function je(e){return e.replace(/\s+/g," ").trim()}function Ve(e,t=140){let n=e.textContent;if(!n)return;let r=je(n);if(r)return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}function Xe(e){let t=1;for(let n=e.previousElementSibling;n;n=n.previousElementSibling)n.tagName===e.tagName&&(t+=1);return t}var Ee=["data-testid","data-test-id","data-test","data-qa","data-cy"],we="data-blocfeed-component";function Ze(e){let t=e.closest(`[${we}]`);if(!t)return;let r=t.getAttribute(we)?.trim();return r||void 0}function We(e){for(let t of Ee){let n=e.closest(`[${t}]`);if(!n)continue;let o=n.getAttribute(t)?.trim();if(o)return o}}function be(e){try{let t=e,n=Object.getOwnPropertyNames(t);for(let r of n)if(r.startsWith("__reactFiber$")||r.startsWith("__reactInternalInstance$")){let o=t[r];if(o&&typeof o=="object")return o}}catch{}return null}function B(e){if(e.length<=1||e.length===2&&e[0]===e[0].toLowerCase())return true;let t=e[0];return t>="a"&&t<="z"}function q(e){if(e){for(let t of e)if(typeof t.name=="string"&&t.name&&!B(t.name))return t.name}}function A(e){if(e&&typeof e!="string"){if(typeof e=="function"){let t=e;return typeof t.displayName=="string"&&t.displayName?t.displayName:typeof t.name=="string"&&t.name?t.name:void 0}if(typeof e=="object"){let t=e,n=t.displayName;if(typeof n=="string"&&n)return n;let r=t.render;if(typeof r=="function"){let i=r;if(typeof i.displayName=="string"&&i.displayName)return i.displayName;if(typeof i.name=="string"&&i.name)return i.name}let o=t.type;return A(o)}}}function Ke(e){let t=be(e);if(!t)return;let n=q(t._debugInfo);if(n)return n;let r=t._debugOwner!==void 0;if(r){let s=t._debugOwner;for(let c=0;s&&c<50;c+=1){let h=q(s._debugInfo);if(h)return h;let p=A(s.type)??A(s.elementType);if(p&&!B(p))return p;s=s._debugOwner;}}if(t._owner!==void 0&&t._owner!==t._debugOwner){let s=t._owner;for(let c=0;s&&c<50;c+=1){let h=q(s._debugInfo);if(h)return h;let p=A(s.type)??A(s.elementType);if(p&&!B(p))return p;s=s._owner;}}let i=t,l=r?80:25;for(let s=0;i&&s<l;s+=1){let c=q(i._debugInfo);if(c)return c;let h=A(i.type)??A(i.elementType);if(h&&!B(h))return h;i=i.return;}let a=e.parentElement;for(let s=0;a&&s<15;s+=1){let c=be(a);if(c){let h=q(c._debugInfo);if(h)return h;let p=A(c.type)??A(c.elementType);if(p&&!B(p))return p;if(c._debugOwner){let m=A(c._debugOwner.type)??A(c._debugOwner.elementType);if(m&&!B(m))return m}if(c._owner&&c._owner!==c._debugOwner){let m=A(c._owner.type)??A(c._owner.elementType);if(m&&!B(m))return m}}a=a.parentElement;}}function Qe(e){let t=e.tagName.toLowerCase(),n=e.getAttribute("id");if(n)return `#${he(n)}`;for(let r of Ee){let o=e.getAttribute(r);if(o)return `${t}[${r}="${he(o)}"]`}return `${t}:nth-of-type(${Xe(e)})`}function Ye(e,t=10){let n=[],r=e;for(;r&&n.length<t;){let o=Qe(r);if(n.unshift(o),o.startsWith("#"))break;r=r.parentElement;}return n.join(" > ")}function ye(e,t){if(!t||t.length===0)return false;for(let n of t)if(e.closest(n))return true;return false}function ne(e,t){if(!e||ye(e,t.ignoreSelectors))return null;let n=e;for(;n;){if(ye(n,t.ignoreSelectors))return null;if(t.isSelectable?.(n)??Ge(n))return n;n=n.parentElement;}return null}function Ge(e){let t=e.tagName;return !(t==="HTML"||t==="BODY")}function Se(e){let t=e.getBoundingClientRect(),n={selector:Ye(e),tagName:e.tagName.toLowerCase(),rect:te(t),pageRect:$e(t)},r=e.getAttribute("id");r&&(n.id=r);let o=e.className;typeof o=="string"&&o.trim()&&(n.className=o);let i=Ve(e);i&&(n.textSnippet=i);let l=Ze(e)??Ke(e);l&&(n.componentName=l);let a=We(e);return a&&(n.testId=a),n}var re=null;async function ke(){return re||(re=import('html-to-image')),re}async function Je(e){return await new Promise((t,n)=>{let r=new Image;r.onload=()=>t({width:r.naturalWidth,height:r.naturalHeight}),r.onerror=()=>n(new Error("Failed to load generated screenshot")),r.src=e;})}async function _e(e,t){let{width:n,height:r}=await Je(e);return {dataUrl:e,mime:t,width:n,height:r}}function O(e){if(e?.aborted)throw new Error("Aborted")}function ve(){return {async captureElement(e,t){if(!E())throw new Error("captureElement can only run in the browser");O(t.signal);let n=await ke();O(t.signal);let r=t.mime==="image/jpeg"?await n.toJpeg(e,{quality:t.quality??.92,pixelRatio:t.pixelRatio}):await n.toPng(e,{pixelRatio:t.pixelRatio});return O(t.signal),await _e(r,t.mime)},async captureFullPage(e){if(!E())throw new Error("captureFullPage can only run in the browser");O(e.signal);let t=document.documentElement,n=Math.max(t.scrollWidth,t.clientWidth),r=Math.max(t.scrollHeight,t.clientHeight),o=Math.min(1,e.maxDimension/Math.max(n,r)),i=Math.max(1,Math.round(n*o)),l=Math.max(1,Math.round(r*o)),a=await ke();O(e.signal);let s=e.mime==="image/jpeg"?await a.toJpeg(t,{width:i,height:l,quality:e.quality??.92,pixelRatio:e.pixelRatio}):await a.toPng(t,{width:i,height:l,pixelRatio:e.pixelRatio});return O(e.signal),await _e(s,e.mime)}}}function et(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return "Unknown error"}}function w(e,t,n){let r={kind:e,message:et(t)};return n&&(r.detail=n),r}var tt=12e3,nt=2048,rt=.92;function Re(){return Date.now()}function ot(e){return new Promise((t,n)=>{let r=()=>n(new Error("Aborted"));if(e.aborted){r();return}e.addEventListener("abort",r,{once:true});})}async function Ae(e,t,n){let r=new Promise((i,l)=>{let a=setTimeout(()=>l(new Error("Timeout")),t);typeof a.unref=="function"&&a.unref();}),o=[e,r];return n&&o.push(ot(n)),await Promise.race(o)}function it(e){if(!E())return 1;let t=window.devicePixelRatio||1,n=e?.pixelRatio??Math.min(t,2);return Math.max(.1,n)}function at(e){return !!(e?.element||e?.fullPage)}function Te(e){let t={mime:e.mime,pixelRatio:e.pixelRatio,maxDimension:e.maxDimension};return e.includeQuality&&(t.quality=e.quality),e.signal&&(t.signal=e.signal),t}async function Pe(e){let{selectionElement:t,capture:n,signal:r}=e;if(!E()||!at(n))return;let o=Re(),i=[],l=n?.timeoutMs??tt,a=n?.maxDimension??nt,s=n?.mime??"image/png",c=n?.quality??rt,h=n?.adapter??ve(),p={},m=it(n);if(n?.element&&t)try{let f=t.getBoundingClientRect(),g=Math.min(1,a/Math.max(f.width,f.height)),k=Math.min(m,m*g),v=await Ae(Promise.resolve(h.captureElement(t,{...Te({mime:s,quality:c,pixelRatio:k,maxDimension:a,includeQuality:s==="image/jpeg",...r?{signal:r}:{}})})),l,r);p.element=v;}catch(f){if(r?.aborted)throw f;i.push(w("capture_failed",f,{target:"element"}));}if(n?.fullPage)try{let f=await Ae(Promise.resolve(h.captureFullPage(Te({mime:s,quality:c,pixelRatio:m,maxDimension:a,includeQuality:s==="image/jpeg",...r?{signal:r}:{}}))),l,r);p.fullPage=f;}catch(f){if(r?.aborted)throw f;i.push(w("capture_failed",f,{target:"fullPage"}));}let b=Re(),u={startedAt:o,finishedAt:b,durationMs:Math.max(0,b-o)};return i.length>0&&(u.errors=i),{...p,diagnostics:u}}function st(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}function ct(){return E()?{url:window.location.href,title:document.title,referrer:document.referrer||void 0,userAgent:navigator.userAgent,language:navigator.language,platform:navigator.platform,viewport:{width:window.innerWidth,height:window.innerHeight},screen:{width:window.screen.width,height:window.screen.height},scroll:{x:window.scrollX,y:window.scrollY},devicePixelRatio:window.devicePixelRatio||1,timezone:st()}:{}}function lt(e){if(!e)return {};let t={};return e.id&&(t.userId=e.id),e.email&&(t.userEmail=e.email),e.name&&(t.userName=e.name),t}async function xe(e){let{config:t,context:n,user:r}=e;if(t?.enabled===false)return {};let o={...ct(),...lt(r)},i=t?.enrich;if(!i)return o;try{let l=await i(n);return {...o,...l}}catch(l){let a=w("unknown",l);return {...o,blocfeedMetadataError:a.message}}}var oe="blocfeed-queue",ut=50;function ie(){if(!E())return [];try{let e=localStorage.getItem(oe);if(!e)return [];let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return []}}function ae(e){if(E())try{e.length===0?localStorage.removeItem(oe):localStorage.setItem(oe,JSON.stringify(e));}catch{}}function dt(e){let t={...e};if(t.screenshots){let n={...t.screenshots};n.element&&(n.element={...n.element,dataUrl:""}),n.fullPage&&(n.fullPage={...n.fullPage,dataUrl:""}),t.screenshots=n;}return delete t.video,t}function se(e){let t=ie(),n=dt(e);for(n.metadata={...n.metadata,_queued:true},t.push({payload:n,timestamp:Date.now()});t.length>ut;)t.shift();ae(t);}function Fe(){let e=ie();return e.length===0?[]:(ae([]),e.map(t=>t.payload))}function Jt(){ae([]);}function en(){return ie().length}var ft=3e4,mt=25e5,Ce="video/webm",pt=250,gt=1e3,ht=["video/webm;codecs=vp9","video/webm;codecs=vp8","video/webm"];function Me(){if(typeof MediaRecorder>"u")return null;for(let e of ht)if(MediaRecorder.isTypeSupported(e))return e;return null}function wt(){return !E()||typeof navigator?.mediaDevices?.getDisplayMedia!="function"?false:Me()!==null}async function ce(e){let{config:t,signal:n}=e;if(!E()||typeof navigator?.mediaDevices?.getDisplayMedia!="function")throw w("recording_failed",new Error("Screen recording is not supported in this browser"));let r=Me();if(!r)throw w("recording_failed",new Error("No supported video codec found (WebM required)"));if(n?.aborted)throw w("aborted",new Error("Recording aborted before start"));let o;try{o=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1});}catch(g){let k=g instanceof DOMException&&g.name==="NotAllowedError"?"Screen share permission denied":"Failed to start screen share";throw w("recording_failed",new Error(k))}if(n?.aborted)throw o.getTracks().forEach(g=>g.stop()),w("aborted",new Error("Recording aborted after permission"));let i=t?.maxDurationMs??ft,l=t?.videoBitsPerSecond??mt,a=new MediaRecorder(o,{mimeType:r,videoBitsPerSecond:l}),s=[],c=[],h=0,p=null,m=null,b=false,u=()=>{p!==null&&(clearInterval(p),p=null),m!==null&&(clearTimeout(m),m=null),o.getTracks().forEach(g=>g.stop());},f=new Promise((g,k)=>{a.ondataavailable=y=>{y.data.size>0&&s.push(y.data);},a.onstop=()=>{if(b)return;b=true,u();let y=Date.now()-h,P=new Blob(s,{type:Ce}),M=URL.createObjectURL(P);g({mime:Ce,blobUrl:M,blob:P,durationMs:y,sizeBytes:P.size});},a.onerror=()=>{b||(b=true,u(),k(w("recording_failed",new Error("MediaRecorder error"))));};let v=o.getVideoTracks()[0];if(v&&v.addEventListener("ended",()=>{a.state!=="inactive"&&a.stop();}),n){let y=()=>{b||(b=true,a.state!=="inactive"&&a.stop(),u(),k(w("aborted",new Error("Recording aborted"))));};n.addEventListener("abort",y,{once:true});}});return a.start(gt),h=Date.now(),p=setInterval(()=>{let g=Date.now()-h;for(let k of c)k(g);},pt),m=setTimeout(()=>{!b&&a.state!=="inactive"&&a.stop();},i),{result:f,stop(){!b&&a.state!=="inactive"&&a.stop();},onTick(g){c.push(g);},abort(){b||(b=true,a.state!=="inactive"&&a.stop(),u());}}}function le(e){let t=null,n=null,r=(...o)=>{n=o,t===null&&(t=requestAnimationFrame(()=>{if(t=null,!n)return;let i=n;n=null,e(...i);}));};return r.cancel=()=>{t!==null&&cancelAnimationFrame(t),t=null,n=null;},r}function K(e){return e instanceof Element?!!e.closest("[data-blocfeed-ui]"):false}function Q(e){e.stopPropagation(),e.stopImmediatePropagation?.();}function Le(e,t){if(!E())throw new Error("BlocFeed picker can only run in a browser environment.");let n=e.ignoreSelectors,r=e.isSelectable,o={};n&&n.length>0&&(o.ignoreSelectors=n),r&&(o.isSelectable=r);let i=null,l=null,a=(u,f=false)=>{if(!u){i=null,l=null,t.onHover(null);return}let g=te(u.getBoundingClientRect()),k=`${Math.round(g.x)}:${Math.round(g.y)}:${Math.round(g.width)}:${Math.round(g.height)}`;!f&&u===i&&k===l||(i=u,l=k,t.onHover({element:u,rect:g}));},s=le(u=>{if(K(u.target))return;let f=document.elementFromPoint(u.clientX,u.clientY),g=ne(f,o);a(g);}),c=le(()=>{i&&a(i,true);}),h=u=>{K(u.target)||(Q(u),u.pointerType==="mouse"&&u.preventDefault());},p=u=>{K(u.target)||(Q(u),u.pointerType==="mouse"&&u.preventDefault());},m=u=>{if(K(u.target))return;Q(u),u.preventDefault();let f=document.elementFromPoint(u.clientX,u.clientY),g=ne(f,o);g&&t.onSelect({element:g,descriptor:Se(g)});},b=u=>{u.key==="Escape"&&(Q(u),u.preventDefault(),t.onCancel());};return window.addEventListener("pointermove",s,{capture:true,passive:true}),window.addEventListener("pointerdown",h,{capture:true}),window.addEventListener("pointerup",p,{capture:true}),window.addEventListener("click",m,{capture:true}),window.addEventListener("keydown",b,{capture:true}),window.addEventListener("scroll",c,{capture:true,passive:true}),window.addEventListener("resize",c,{passive:true}),{stop(){window.removeEventListener("pointermove",s,{capture:true}),window.removeEventListener("pointerdown",h,{capture:true}),window.removeEventListener("pointerup",p,{capture:true}),window.removeEventListener("click",m,{capture:true}),window.removeEventListener("keydown",b,{capture:true}),window.removeEventListener("scroll",c,{capture:true}),window.removeEventListener("resize",c),s.cancel(),c.cancel(),t.onHover(null);}}}var bt=12e3,yt=2,Et=500,St=2e3,ue="https://blocfeed.com/api/feedback",De=0;function Be(e,t){return new Promise((n,r)=>{if(t?.aborted){r(new Error("Aborted"));return}let o=setTimeout(n,e),i=()=>{clearTimeout(o),r(new Error("Aborted"));};t?.addEventListener("abort",i,{once:true});})}function kt(e){return e>=500&&e<=599}function _t(e){let[t,n]=e.split(",",2);if(!t||!n)throw new Error("Invalid data URL");let o=/data:(.*?);base64/.exec(t)?.[1]||"application/octet-stream",i=atob(n),l=new Uint8Array(i.length);for(let a=0;a<i.length;a+=1)l[a]=i.charCodeAt(a);return new Blob([l],{type:o})}function vt(e){let t={},n={},r={...e};if(r.screenshots){let o={},i={...r.screenshots};i.element&&(t.element=i.element.dataUrl,o.element={mime:i.element.mime,width:i.element.width,height:i.element.height},i.element={...i.element,dataUrl:""}),i.fullPage&&(t.fullPage=i.fullPage.dataUrl,o.fullPage={mime:i.fullPage.mime,width:i.fullPage.width,height:i.fullPage.height},i.fullPage={...i.fullPage,dataUrl:""}),r.screenshots=i,(o.element||o.fullPage)&&(r.screenshot_intent=o);}return r.video&&(n.blob=r.video.blob,r.video_intent={mime:r.video.mime,durationMs:r.video.durationMs,sizeBytes:r.video.sizeBytes},delete r.video),{lean:r,extracted:t,extractedVideo:n}}async function Ie(e,t,n){let r=_t(t);await fetch(e,{method:"PUT",body:r,headers:{"content-type":r.type},...n?{signal:n}:{}});}async function Rt(e){let{feedbackId:t,extracted:n,screenshots:r,signal:o}=e,i={};n.element&&r?.element&&(i.element={dataUrl:n.element,mime:r.element.mime,width:r.element.width,height:r.element.height}),n.fullPage&&r?.fullPage&&(i.fullPage={dataUrl:n.fullPage,mime:r.fullPage.mime,width:r.fullPage.width,height:r.fullPage.height}),Object.keys(i).length!==0&&await fetch(`${ue}/${t}/screenshots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(i),...o?{signal:o}:{}});}async function At(e){await fetch(`${ue}/${e.feedbackId}/video`,{method:"POST",body:e.blob,headers:{"content-type":e.blob.type},...e.signal?{signal:e.signal}:{}});}async function Ne(e){let{signal:t,transport:n}=e;if(Date.now()-De<St)return {ok:false,error:w("configuration",new Error("Please wait before submitting again"))};let o=n?.timeoutMs??bt,i=n?.maxAttempts??yt,l=n?.backoffMs??Et,a=!!(e.payload.screenshots?.element?.dataUrl||e.payload.screenshots?.fullPage?.dataUrl),s=!!e.payload.video?.blob,c=a||s,{lean:h,extracted:p,extractedVideo:m}=c?vt(e.payload):{lean:e.payload,extracted:{},extractedVideo:{}},b={...p,...e.screenshotDataUrls};for(let u=1;u<=i;u+=1){let f=new AbortController,g=setTimeout(()=>f.abort(),o),k=()=>f.abort();t&&t.addEventListener("abort",k,{once:true});try{let v=await fetch(ue,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(h),signal:f.signal});if(v.ok){De=Date.now();let y;try{y=await v.json();}catch{}if((b.element||b.fullPage)&&y){let d=y.upload_urls;if(d){let S=[];b.element&&d.element&&S.push(Ie(d.element,b.element,t)),b.fullPage&&d.fullPage&&S.push(Ie(d.fullPage,b.fullPage,t));try{await Promise.all(S);}catch{}}else if(y.feedback_id)try{await Rt({feedbackId:y.feedback_id,extracted:b,screenshots:e.payload.screenshots,...t?{signal:t}:{}});}catch{}}if(m.blob&&y){let d=y.upload_urls;if(d?.video)try{await fetch(d.video,{method:"PUT",body:m.blob,headers:{"content-type":m.blob.type},...t?{signal:t}:{}});}catch{}else if(y.feedback_id)try{await At({feedbackId:y.feedback_id,blob:m.blob,...t?{signal:t}:{}});}catch{}}let M={ok:!0,status:v.status};return y&&(M.apiResponse=y),M}if(u<i&&kt(v.status)){let y=.85+Math.random()*.3,P=Math.round(l*2**(u-1)*y);await Be(P,t);continue}return {ok:!1,status:v.status,error:w("api_failed",new Error(`HTTP ${v.status}`))}}catch(v){if(f.signal.aborted||t?.aborted)return {ok:false,error:w("aborted",v)};if(u<i){let y=.85+Math.random()*.3,P=Math.round(l*2**(u-1)*y);await Be(P,t);continue}return {ok:false,error:w("api_failed",v)}}finally{clearTimeout(g),t&&t.removeEventListener("abort",k);}}return {ok:false,error:w("api_failed",new Error("Failed"))}}async function de(e){let{signal:t,transport:n}=e,r={ok:false};try{let o={payload:e.payload,...t?{signal:t}:{},...n?{transport:n}:{}};r.api=await Ne(o),r.ok=!!r.api?.ok;}catch(o){r.api={ok:false,error:w("api_failed",o)},r.ok=false;}return {payload:e.payload,result:r}}var Tt=["[data-blocfeed-ui]","[data-blocfeed-ignore]"];function Pt(e){let t=[...Tt,...e?.ignoreSelectors??[]],n=Array.from(new Set(t));return {...e,ignoreSelectors:n}}function Oe(){return {phase:"idle"}}function xt(e){if(e.ok)return false;let t=e.api;return t?t.status&&t.status>=400&&t.status<500||t.error?.kind==="aborted"||t.error?.kind==="configuration"?false:!t.ok:true}function Rn(e){let t=e,n=Oe(),r=new Set,o=new Set,i=null,l=null,a=null,s=null,c=0,h=null,p=null,m=null,b=()=>{for(let d of r)d(n);},u=d=>{for(let S of o)S(d);},f=d=>{n=d,b();},g=()=>{c+=1,s?.abort(),s=null;},k=()=>{i?.stop(),i=null,u(null),a!==null&&E()&&(document.documentElement.style.cursor=a,a=null);},v=()=>{p&&(p.abort(),p=null),m&&(URL.revokeObjectURL(m.blobUrl),m=null);},y=()=>{g(),k(),v(),l=null,f(Oe());},P=()=>{if(!E())return;k(),l=null;let d=Pt(t.picker);a=document.documentElement.style.cursor,document.documentElement.style.cursor="crosshair",f({phase:"picking"}),i=Le(d,{onHover:u,onSelect:({element:S,descriptor:x})=>{l=S,k(),f({phase:"review",selection:x});},onCancel:()=>{y();}});},M=()=>{let d=Fe();if(d.length!==0)for(let S of d)de({payload:S,...t.transport?{transport:t.transport}:{}}).catch(()=>{se(S);});};if(E()){setTimeout(M,1e3);let d=()=>M();window.addEventListener("online",d),h=()=>window.removeEventListener("online",d);}return {getState:()=>n,subscribe(d){return r.add(d),()=>r.delete(d)},subscribeHover(d){return o.add(d),()=>o.delete(d)},start(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||n.phase!=="picking"&&P();},stop(){y();},clearSelection(){n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording"||P();},setConfig(d){t=d;},async submit(d,S){if(!E()){let R=w("configuration",new Error("BlocFeed submit can only run in the browser"));return f({phase:"error",lastError:R}),{ok:false}}let x=t.blocfeed_id?.trim?.()??"";if(!x){let L={phase:"error",lastError:w("configuration",new Error("Missing blocfeed_id. Create a project in BlocFeed and pass its blocfeed_id."))};return n.selection&&(L.selection=n.selection),f(L),{ok:false}}if(n.phase==="capturing"||n.phase==="submitting"||n.phase==="recording")return {ok:false};let F=c+1;c=F,s?.abort(),s=new AbortController;let T=s.signal,_=n.selection,U=S?.capture?{...t.capture,...S.capture}:t.capture,H=!!(U?.element||U?.fullPage),ge={phase:H?"capturing":"submitting"};_&&(ge.selection=_),f(ge);try{let R=H?await Pe({selectionElement:l,capture:U,signal:T}):void 0;if(T.aborted||c!==F)return {ok:!1};let L={phase:"submitting"};_&&(L.selection=_),R&&(L.capture=R),f(L);let N={};_&&(N.selection=_),R&&(N.capture=R);let qe=await xe({config:t.metadata,context:N,...t.user?{user:t.user}:{}}),D={version:1,createdAt:new Date().toISOString(),blocfeed_id:x,message:d,metadata:qe};S?.category&&(D.category=S.category),t.user&&(D.user=t.user),_&&(D.selection=_),R&&(D.screenshots=R),m&&(D.video=m);let{result:C}=await de({payload:D,signal:T,...t.transport?{transport:t.transport}:{}});if(T.aborted||c!==F)return C;if(C.ok){m&&(URL.revokeObjectURL(m.blobUrl),m=null);let ee={phase:"success",lastSubmit:C};return _&&(ee.selection=_),R&&(ee.capture=R),f(ee),C}xt(C)&&se(D);let ze=C.api?.error??w("unknown",new Error("Submission failed")),J={phase:"error",lastSubmit:C,lastError:ze};return _&&(J.selection=_),R&&(J.capture=R),f(J),C}catch(R){if(T.aborted||c!==F)return {ok:false};let N={phase:"error",lastError:T.aborted?w("aborted",R):w("unknown",R)};return _&&(N.selection=_),f(N),{ok:false}}finally{c===F&&(s=null);}},async startRecording(){if(n.phase!=="review"||!t.recording?.enabled)return;let d=n.selection,S={phase:"recording",recordingElapsedMs:0};d&&(S.selection=d),f(S);try{let x={config:t.recording};s&&(x.signal=s.signal);let F=await ce(x);p=F,F.onTick(U=>{if(n.phase==="recording"){let H={phase:"recording",recordingElapsedMs:U};d&&(H.selection=d),f(H);}});let T=await F.result;p=null,m=T;let _={phase:"review",video:T};d&&(_.selection=d),f(_);}catch(x){p=null;let T={phase:"review",lastError:x?.kind?x:w("recording_failed",x)};d&&(T.selection=d),f(T);}},stopRecording(){n.phase!=="recording"||!p||p.stop();},removeVideo(){m&&(URL.revokeObjectURL(m.blobUrl),m=null);let d=n.selection,S={phase:"review"};d&&(S.selection=d),f(S);},__unsafeGetSelectedElement(){return l},destroy(){y(),r.clear(),o.clear(),h?.(),h=null;}}}var z=[],$=[],Ue=20,He=15,Y=[],j={},V=null,X=null,Z=null,G=false,Ft=["blocfeed.com","google-analytics.com","googletagmanager.com","analytics.google.com","googleads.g.doubleclick.net","googlesyndication.com","googleadservices.com","google.com/pagead","google.com/ads","facebook.net","facebook.com/tr","connect.facebook.net","graph.facebook.com","api.mixpanel.com","api-js.mixpanel.com","api.amplitude.com","api2.amplitude.com","api.segment.io","cdn.segment.com","vars.hotjar.com","in.hotjar.com","script.hotjar.com","heapanalytics.com","fullstory.com/s/fs.js","rs.fullstory.com","app.posthog.com","us.posthog.com","eu.posthog.com","api-iam.intercom.io","widget.intercom.io","sentry.io/api","browser.sentry-cdn.com","browser-intake-datadoghq.com","clarity.ms","js.hs-analytics.net","api.hubapi.com","forms.hubspot.com","plausible.io/api","client.crisp.chat","js.driftt.com","r.logrocket.com","app.pendo.io","events.launchdarkly.com","app.launchdarkly.com","grammarly.com","gnar.grammarly.com","capi.grammarly.com","api.languagetool.org","api.languagetoolplus.com","fonts.googleapis.com","fonts.gstatic.com","cdn.cookielaw.org","consent.cookiebot.com","js.stripe.com","api.stripe.com/v1/tokens","google.com/recaptcha","hcaptcha.com","bam.nr-data.net","rec.smartlook.com","o2.mouseflow.com","api.luckyorange.com","static.zdassets.com","ekr.zdassets.com"];function Ct(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function Mt(e,t){let n=t.map(Ct).join(" "),r=t.find(i=>i instanceof Error),o={level:e,message:n.slice(0,2e3),timestamp:Date.now()};for(r?.stack&&(o.stack=r.stack.slice(0,2e3)),z.push(o);z.length>Ue;)z.shift();}function fe(e){let t=e.url.toLowerCase();for(let n of Y)if(t.includes(n))return;for($.push(e);$.length>He;)$.shift();}function Pn(e={}){if(G||!E())return;G=true,Ue=e.consoleLimit??20,He=e.networkLimit??15;let t=e.ignoreUrls;if(t!==void 0?Y=t.map(n=>n.toLowerCase()):Y=[...Ft],e.console!==false){let n=e.consoleLevels??["error","warn"];for(let r of n)j[r]=console[r],console[r]=(...o)=>{Mt(r,o),j[r]?.apply(console,o);};}if(e.network!==false&&typeof window.fetch=="function"){V=window.fetch;let n=V;window.fetch=async function(o,i){let l=typeof o=="string"?o:o instanceof URL?o.toString():o.url,a=(i?.method??"GET").toUpperCase(),s=Date.now();try{let c=await n.call(window,o,i);return c.ok||fe({url:l.slice(0,500),method:a,status:c.status,statusText:c.statusText,timestamp:s,durationMs:Date.now()-s}),c}catch(c){throw fe({url:l.slice(0,500),method:a,status:0,statusText:c instanceof Error?c.message:"Network error",timestamp:s,durationMs:Date.now()-s}),c}};}if(e.network!==false&&typeof XMLHttpRequest<"u"){X=XMLHttpRequest.prototype.open,Z=XMLHttpRequest.prototype.send;let n=X,r=Z;XMLHttpRequest.prototype.open=function(o,i,...l){return this.__bf_method=o.toUpperCase(),this.__bf_url=String(i),n.apply(this,[o,i,...l])},XMLHttpRequest.prototype.send=function(...o){let i=this.__bf_method||"GET",l=this.__bf_url||"",a=Date.now();return this.addEventListener("loadend",function(){if(this.status>=400||this.status===0){let s={url:l.slice(0,500),method:i,status:this.status,timestamp:a,durationMs:Date.now()-a};this.statusText&&(s.statusText=this.statusText),fe(s);}}),r.apply(this,o)};}}function xn(){if(G){for(let[e,t]of Object.entries(j))console[e]=t;for(let e of Object.keys(j))delete j[e];V&&(window.fetch=V,V=null),X&&(XMLHttpRequest.prototype.open=X,X=null),Z&&(XMLHttpRequest.prototype.send=Z,Z=null),Y=[],G=false;}}function Fn(){return {consoleLogs:[...z],networkErrors:[...$]}}function Cn(){z=[],$=[];}var Lt=[{name:"supabase_service_role_key",pattern:/SUPABASE_SERVICE_ROLE_KEY["'=:\s]+[A-Za-z0-9_.=-]{30,}/},{name:"supabase_db_password",pattern:/SUPABASE_DB_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"postgres_password",pattern:/POSTGRES_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"database_url_with_creds",pattern:/(?:postgres|postgresql|mysql|mongodb|redis|amqp):\/\/[^:]+:[^@\s"']+@/},{name:"aws_access_key",pattern:/AKIA[0-9A-Z]{16}/},{name:"aws_secret_key",pattern:/AWS_SECRET_ACCESS_KEY["'=:\s]+[A-Za-z0-9/+=]{30,}/},{name:"aws_session_token",pattern:/AWS_SESSION_TOKEN["'=:\s]+[A-Za-z0-9/+=]{30,}/},{name:"stripe_secret_key",pattern:/sk_live_[a-zA-Z0-9]{10,}/},{name:"stripe_secret_key_test",pattern:/sk_test_[a-zA-Z0-9]{10,}/},{name:"stripe_restricted_key",pattern:/rk_(?:live|test)_[a-zA-Z0-9]{10,}/},{name:"github_pat",pattern:/ghp_[A-Za-z0-9_]{36,}/},{name:"github_oauth",pattern:/gho_[A-Za-z0-9_]{36,}/},{name:"github_user_token",pattern:/ghu_[A-Za-z0-9_]{36,}/},{name:"github_server_token",pattern:/ghs_[A-Za-z0-9_]{36,}/},{name:"github_fine_grained",pattern:/github_pat_[A-Za-z0-9_]{22,}/},{name:"openai_api_key",pattern:/sk-[a-zA-Z0-9]{20,}(?![a-zA-Z0-9_])/},{name:"anthropic_api_key",pattern:/sk-ant-[a-zA-Z0-9\-_]{20,}/},{name:"private_key",pattern:/-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----/},{name:"sendgrid_api_key",pattern:/SG\.[a-zA-Z0-9_-]{22,}\.[a-zA-Z0-9_-]{22,}/},{name:"twilio_auth_token",pattern:/TWILIO_AUTH_TOKEN["'=:\s]+[a-f0-9]{32}/},{name:"generic_secret_key",pattern:/[A-Z_]{2,}_SECRET_KEY["'=:\s]+[^\s"']{8,}/},{name:"generic_secret",pattern:/[A-Z_]{2,}_SECRET["'=:\s]+[^\s"']{8,}/},{name:"generic_password",pattern:/[A-Z_]{2,}_PASSWORD["'=:\s]+[^\s"']{6,}/},{name:"generic_private_key_var",pattern:/[A-Z_]{2,}_PRIVATE_KEY["'=:\s]+[^\s"']{8,}/}],I=[],pe=0,me=false;function Dt(e){let t=e.slice(0,80);return t.length<=6?"***":t.slice(0,6)+"***"}function Bt(e,t,n,r){if(I.some(l=>l.rule===e&&l.source===t))return;let i={rule:e,source:t,hint:Dt(n),timestamp:Date.now()};r&&(i.location=r),I.push(i);}function W(e,t,n,r){for(let{name:o,pattern:i}of n){let l=i.exec(e);l&&Bt(o,t,l[0],r);}}function It(e){try{let t=window;if(t.__NEXT_DATA__){let n=JSON.stringify(t.__NEXT_DATA__);W(n,"hydration",e,"__NEXT_DATA__");}if(t.__NUXT__){let n=JSON.stringify(t.__NUXT__);W(n,"hydration",e,"__NUXT__");}}catch{}}function Nt(e){try{document.querySelectorAll("script:not([src])").forEach((n,r)=>{let o=n.textContent;o&&o.length>0&&W(o,"scripts",e,`inline-script[${r}]`);});}catch{}}function Ot(e){try{document.querySelectorAll("meta[content]").forEach(n=>{let r=n.getAttribute("content"),o=n.getAttribute("name")||n.getAttribute("property")||"";r&&r.length>10&&W(r,"meta",e,o?`meta[${o}]`:void 0);});}catch{}}function Ut(e){try{document.querySelectorAll("[data-api-key], [data-secret], [data-token], [data-password]").forEach(n=>{let r=n.attributes;for(let o=0;o<r.length;o++){let i=r[o];i.name.startsWith("data-")&&i.value.length>10&&W(i.value,"dom",e,`${n.tagName.toLowerCase()}[${i.name}]`);}});}catch{}}function Dn(e={}){if(me||!E()||(me=true,e.secretScan===false))return;let t=[...Lt,...e.customPatterns??[]],n=e.scanTargets??["hydration","scripts","meta","dom"];setTimeout(()=>{pe=Date.now(),n.includes("hydration")&&It(t),n.includes("scripts")&&Nt(t),n.includes("meta")&&Ot(t),n.includes("dom")&&Ut(t);let r=e.notify??"both";I.length>0&&(r==="console"||r==="both")&&console.warn(`[BlocFeed Security] Found ${I.length} potential secret(s) exposed in client code:`,I.map(o=>`${o.rule} (${o.source}${o.location?`: ${o.location}`:""})`));},100);}function Bn(){return {findings:[...I],scannedAt:pe}}function In(){I=[],pe=0,me=false;}
|
|
2
|
+
exports.a=E;exports.b=te;exports.c=ve;exports.d=Pe;exports.e=xe;exports.f=se;exports.g=Fe;exports.h=Jt;exports.i=en;exports.j=wt;exports.k=ce;exports.l=Rn;exports.m=Pn;exports.n=xn;exports.o=Fn;exports.p=Cn;exports.q=Dn;exports.r=Bn;exports.s=In;
|
|
@@ -38,6 +38,8 @@ interface BlocFeedHandle {
|
|
|
38
38
|
open: () => void;
|
|
39
39
|
close: () => void;
|
|
40
40
|
submit: (message: string) => Promise<SubmitResult>;
|
|
41
|
+
startRecording: () => Promise<void>;
|
|
42
|
+
stopRecording: () => void;
|
|
41
43
|
isOpen: boolean;
|
|
42
44
|
}
|
|
43
45
|
interface BlocFeedStrings {
|
|
@@ -59,6 +61,12 @@ interface BlocFeedStrings {
|
|
|
59
61
|
categoryFeature?: string;
|
|
60
62
|
categoryUx?: string;
|
|
61
63
|
categoryGeneral?: string;
|
|
64
|
+
recordButton?: string;
|
|
65
|
+
stopRecordButton?: string;
|
|
66
|
+
recordingText?: string;
|
|
67
|
+
recordingDeniedText?: string;
|
|
68
|
+
videoPreviewLabel?: string;
|
|
69
|
+
removeVideoButton?: string;
|
|
62
70
|
}
|
|
63
71
|
interface ConsoleEntry {
|
|
64
72
|
level: "error" | "warn" | "log";
|
|
@@ -85,6 +93,13 @@ interface DiagnosticsConfig {
|
|
|
85
93
|
network?: boolean;
|
|
86
94
|
/** Max network entries to retain. Default: 15 */
|
|
87
95
|
networkLimit?: number;
|
|
96
|
+
/**
|
|
97
|
+
* URL patterns to exclude from network capture.
|
|
98
|
+
* Each entry is matched as a substring against the request URL.
|
|
99
|
+
* Common analytics/tracking domains are excluded by default.
|
|
100
|
+
* Set to `[]` to disable the built-in blocklist and capture everything.
|
|
101
|
+
*/
|
|
102
|
+
ignoreUrls?: string[];
|
|
88
103
|
}
|
|
89
104
|
type SecurityScanTarget = "hydration" | "scripts" | "meta" | "dom";
|
|
90
105
|
interface SecurityConfig {
|
|
@@ -155,7 +170,7 @@ interface CaptureResult {
|
|
|
155
170
|
diagnostics: CaptureDiagnostics;
|
|
156
171
|
}
|
|
157
172
|
interface BlocFeedError {
|
|
158
|
-
kind: "unknown" | "configuration" | "capture_failed" | "api_failed" | "aborted";
|
|
173
|
+
kind: "unknown" | "configuration" | "capture_failed" | "recording_failed" | "api_failed" | "aborted";
|
|
159
174
|
message: string;
|
|
160
175
|
detail?: Record<string, unknown>;
|
|
161
176
|
}
|
|
@@ -177,6 +192,33 @@ interface ScreenshotAdapter {
|
|
|
177
192
|
captureElement: (element: Element, options: ScreenshotAdapterOptions) => MaybePromise<ImageAsset>;
|
|
178
193
|
captureFullPage: (options: ScreenshotAdapterOptions) => MaybePromise<ImageAsset>;
|
|
179
194
|
}
|
|
195
|
+
type VideoMime = "video/webm";
|
|
196
|
+
interface RecordingConfig {
|
|
197
|
+
/** Enable video recording button in the widget. Default: false */
|
|
198
|
+
enabled?: boolean;
|
|
199
|
+
/** Maximum recording duration in milliseconds. Default: 30 000 (30 s) */
|
|
200
|
+
maxDurationMs?: number;
|
|
201
|
+
/** Video MIME type. Default: "video/webm" */
|
|
202
|
+
mime?: VideoMime;
|
|
203
|
+
/** Video bitrate in bits/s. Default: 2 500 000 (2.5 Mbps) */
|
|
204
|
+
videoBitsPerSecond?: number;
|
|
205
|
+
}
|
|
206
|
+
interface VideoAsset {
|
|
207
|
+
mime: VideoMime;
|
|
208
|
+
/** Object URL (blob:) for local preview. Revoked after submission. */
|
|
209
|
+
blobUrl: string;
|
|
210
|
+
/** The raw Blob — held in memory until uploaded. */
|
|
211
|
+
blob: Blob;
|
|
212
|
+
/** Duration of the recording in milliseconds. */
|
|
213
|
+
durationMs: number;
|
|
214
|
+
/** Byte size of the video blob. */
|
|
215
|
+
sizeBytes: number;
|
|
216
|
+
}
|
|
217
|
+
interface VideoIntent {
|
|
218
|
+
mime: string;
|
|
219
|
+
durationMs: number;
|
|
220
|
+
sizeBytes: number;
|
|
221
|
+
}
|
|
180
222
|
interface CaptureConfig {
|
|
181
223
|
element?: boolean;
|
|
182
224
|
fullPage?: boolean;
|
|
@@ -235,6 +277,8 @@ interface BlocFeedConfig {
|
|
|
235
277
|
diagnostics?: DiagnosticsConfig;
|
|
236
278
|
/** Client-side secret leak detection. */
|
|
237
279
|
security?: SecurityConfig;
|
|
280
|
+
/** Video recording configuration. */
|
|
281
|
+
recording?: RecordingConfig;
|
|
238
282
|
ui?: {
|
|
239
283
|
/** z-index for the widget overlay/panel. */
|
|
240
284
|
zIndex?: number;
|
|
@@ -286,6 +330,10 @@ interface FeedbackPayload {
|
|
|
286
330
|
screenshots?: CaptureResult;
|
|
287
331
|
/** Lightweight screenshot metadata sent instead of base64 dataUrls. */
|
|
288
332
|
screenshot_intent?: ScreenshotIntent;
|
|
333
|
+
/** Video recording asset (blob held separately during transport). */
|
|
334
|
+
video?: VideoAsset;
|
|
335
|
+
/** Lightweight video metadata sent instead of the blob. */
|
|
336
|
+
video_intent?: VideoIntent;
|
|
289
337
|
/** First-class user identity. */
|
|
290
338
|
user?: BlocFeedUser;
|
|
291
339
|
metadata: Record<string, unknown>;
|
|
@@ -296,6 +344,7 @@ interface FeedbackApiResponse {
|
|
|
296
344
|
upload_urls?: {
|
|
297
345
|
element?: string;
|
|
298
346
|
fullPage?: string;
|
|
347
|
+
video?: string;
|
|
299
348
|
};
|
|
300
349
|
}
|
|
301
350
|
interface TransportResult {
|
|
@@ -307,11 +356,13 @@ interface SubmitResult {
|
|
|
307
356
|
ok: boolean;
|
|
308
357
|
api?: TransportResult;
|
|
309
358
|
}
|
|
310
|
-
type SessionPhase = "idle" | "picking" | "review" | "capturing" | "submitting" | "success" | "error";
|
|
359
|
+
type SessionPhase = "idle" | "picking" | "review" | "recording" | "capturing" | "submitting" | "success" | "error";
|
|
311
360
|
interface BlocFeedState {
|
|
312
361
|
phase: SessionPhase;
|
|
313
362
|
selection?: ElementDescriptor;
|
|
314
363
|
capture?: CaptureResult;
|
|
364
|
+
video?: VideoAsset;
|
|
365
|
+
recordingElapsedMs?: number;
|
|
315
366
|
lastSubmit?: SubmitResult;
|
|
316
367
|
lastError?: BlocFeedError;
|
|
317
368
|
}
|
|
@@ -335,10 +386,13 @@ interface BlocFeedController {
|
|
|
335
386
|
capture?: CaptureConfig;
|
|
336
387
|
category?: FeedbackCategory;
|
|
337
388
|
}) => Promise<SubmitResult>;
|
|
389
|
+
startRecording: () => Promise<void>;
|
|
390
|
+
stopRecording: () => void;
|
|
391
|
+
removeVideo: () => void;
|
|
338
392
|
/** Internal: used by UI to access the live element handle. */
|
|
339
393
|
__unsafeGetSelectedElement: () => Element | null;
|
|
340
394
|
destroy: () => void;
|
|
341
395
|
}
|
|
342
396
|
declare function createBlocFeedController(config: BlocFeedConfig): BlocFeedController;
|
|
343
397
|
|
|
344
|
-
export { type BlocFeedConfig as B, type CaptureConfig as C, type DiagnosticsConfig as D, type ElementDescriptor as E, type FeedbackCategory as F, type HoverListener as H, type ImageAsset as I, type MaybePromise as M, type NetworkEntry as N, type PickerConfig as P, type
|
|
398
|
+
export { type VideoMime as A, type BlocFeedConfig as B, type CaptureConfig as C, type DiagnosticsConfig as D, type ElementDescriptor as E, type FeedbackCategory as F, type StateListener as G, type HoverListener as H, type ImageAsset as I, createBlocFeedController as J, type MaybePromise as M, type NetworkEntry as N, type PickerConfig as P, type RecordingConfig as R, type SubmitResult as S, type ThemeConfig as T, type VideoAsset as V, type WidgetPosition as W, type BlocFeedHandle as a, type BlocFeedState as b, type BlocFeedController as c, type BlocFeedError as d, type BlocFeedStrings as e, type BlocFeedUser as f, type CaptureDiagnostics as g, type CaptureResult as h, type ConsoleEntry as i, type FeedbackApiResponse as j, type FeedbackPayload as k, type MetadataConfig as l, type MetadataContext as m, type Rect as n, type ScreenshotAdapter as o, type ScreenshotAdapterOptions as p, type ScreenshotIntent as q, type ScreenshotMime as r, type SecurityConfig as s, type SecurityFinding as t, type SecuritySnapshot as u, type SessionPhase as v, type TransportConfig as w, type TransportResult as x, type TriggerStyle as y, type VideoIntent as z };
|