blocfeed 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +206 -8
- package/dist/chunk-JVY6JTXP.cjs +2 -0
- package/dist/chunk-LAK7UUTC.js +2 -0
- package/dist/{controller-DfhbkHXc.d.cts → controller-BPsU7cuY.d.cts} +58 -1
- package/dist/{controller-DfhbkHXc.d.ts → controller-BPsU7cuY.d.ts} +58 -1
- package/dist/engine.cjs +1 -1
- package/dist/engine.d.cts +16 -3
- package/dist/engine.d.ts +16 -3
- package/dist/engine.js +1 -1
- package/dist/main.cjs +139 -3
- package/dist/main.d.cts +5 -4
- package/dist/main.d.ts +5 -4
- package/dist/main.js +139 -3
- package/package.json +1 -1
- package/dist/chunk-D5SBICBQ.cjs +0 -2
- package/dist/chunk-QPAFC4IL.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.1 — 2026-02-21
|
|
4
|
+
|
|
5
|
+
### New features
|
|
6
|
+
|
|
7
|
+
- **Client-side secret leak scanner** — `config.security` detects accidentally exposed API keys, database credentials, and tokens in client-side code. Scans Next.js `__NEXT_DATA__`, Nuxt `__NUXT__`, inline scripts, meta tags, and DOM attributes. Supports 20+ known patterns including:
|
|
8
|
+
- **Supabase / Postgres**: `SUPABASE_SERVICE_ROLE_KEY`, `POSTGRES_PASSWORD`, `DATABASE_URL` with embedded credentials
|
|
9
|
+
- **AWS**: Access keys (`AKIA...`), secret keys, session tokens
|
|
10
|
+
- **Stripe**: Secret keys (`sk_live_...`, `sk_test_...`), restricted keys
|
|
11
|
+
- **GitHub**: PATs, OAuth tokens, fine-grained tokens
|
|
12
|
+
- **OpenAI / Anthropic**: API keys
|
|
13
|
+
- **Infrastructure**: Private keys, database URLs with credentials, SendGrid, Twilio
|
|
14
|
+
- **Generic**: Any env var ending in `_SECRET`, `_PASSWORD`, `_PRIVATE_KEY`
|
|
15
|
+
- **Custom patterns**: `config.security.customPatterns` for app-specific secret formats
|
|
16
|
+
- **Security warning banner**: Visual alert when secrets are detected, dismissible by user
|
|
17
|
+
- **Redacted findings**: Secret values are never transmitted — only the first 6 characters + `***`
|
|
18
|
+
- **Dashboard reporting**: Findings attached as `metadata._securityFindings` in every feedback submission
|
|
19
|
+
|
|
20
|
+
### Improvements
|
|
21
|
+
|
|
22
|
+
- New types exported: `SecurityConfig`, `SecurityFinding`, `SecuritySnapshot`.
|
|
23
|
+
- Engine exports: `runSecretScan`, `getSecurityFindings`, `clearSecurityFindings` for headless usage.
|
|
24
|
+
- 14 new tests for secret scanning (80 total).
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 0.7.0 — 2026-02-20
|
|
29
|
+
|
|
30
|
+
### New features
|
|
31
|
+
|
|
32
|
+
- **XHR network capture** — Network diagnostics now intercept `XMLHttpRequest` in addition to `fetch`. Apps using axios, jQuery, or other XHR-based libraries get full network error capture.
|
|
33
|
+
- **Conditional display (`showOn`)** — `config.ui.showOn` restricts widget visibility to specific routes. Accepts an array of route patterns (with wildcard `*` suffix) or a custom predicate function. Default: show on all pages.
|
|
34
|
+
- **Programmatic API** — `BlocFeedWidget` now accepts a React ref exposing `open()`, `close()`, `submit(message)`, and `isOpen`. New `BlocFeedHandle` type exported for TypeScript consumers.
|
|
35
|
+
- **Feedback categories** — Pill selector in the feedback form: Bug, Feature, UX, General. Selection is included as `category` in the payload. Configurable via `config.ui.categories` and fully localizable via `config.ui.strings`.
|
|
36
|
+
- **Dark / Light mode** — `config.ui.theme.mode` supports `"light"`, `"dark"`, or `"auto"` (uses `prefers-color-scheme`). Default remains `"dark"`. Light theme includes fully styled panel, textarea, highlight, and overlay.
|
|
37
|
+
|
|
38
|
+
### Improvements
|
|
39
|
+
|
|
40
|
+
- New types exported: `BlocFeedHandle`, `FeedbackCategory`.
|
|
41
|
+
- `BlocFeedStrings` extended with `categoryBug`, `categoryFeature`, `categoryUx`, `categoryGeneral`.
|
|
42
|
+
- `FeedbackPayload` now includes optional `category` field.
|
|
43
|
+
- SPA route detection for `showOn`: patches `history.pushState` / `replaceState` and listens for `popstate`.
|
|
44
|
+
- Backlog updated: shipped items tracked, remaining ideas reorganized.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
3
48
|
## 0.6.1 — 2026-02-20
|
|
4
49
|
|
|
5
50
|
- Re-publish of 0.6.0 with all features included (diagnostics, i18n, watermark).
|
package/README.md
CHANGED
|
@@ -11,6 +11,11 @@ Drop-in in-app feedback widget for **Next.js** and **React**:
|
|
|
11
11
|
- **Offline queue** — failed submissions are stored and retried automatically
|
|
12
12
|
- **Customizable position, theme, and retry behavior**
|
|
13
13
|
- **Accessible** — keyboard navigation, focus trapping, ARIA attributes
|
|
14
|
+
- **Feedback categories** — pill selector (Bug, Feature, UX, General) included in payload
|
|
15
|
+
- **Dark / Light mode** — `"dark"`, `"light"`, or `"auto"` (follows system preference)
|
|
16
|
+
- **Conditional display** — show/hide widget by route pattern or custom predicate
|
|
17
|
+
- **Programmatic API** — `ref.open()`, `ref.close()`, `ref.submit(msg)` via React ref
|
|
18
|
+
- **Secret leak detection** — scans client-side code for exposed API keys, database credentials, and tokens
|
|
14
19
|
|
|
15
20
|
Docs live in `docs/` (start at `docs/index.md`). Architecture pointers are in `ARCHITECTURE.md`.
|
|
16
21
|
|
|
@@ -114,13 +119,16 @@ All configuration is passed via the `config` prop on `<BlocFeedWidget>` or `<Blo
|
|
|
114
119
|
panelBackground: "rgba(0, 0, 0, 0.95)",
|
|
115
120
|
panelForeground: "#ffffff",
|
|
116
121
|
fontFamily: "Inter, sans-serif",
|
|
122
|
+
mode: "dark", // "dark" | "light" | "auto"
|
|
117
123
|
},
|
|
124
|
+
categories: ["bug", "feature", "ux", "general"], // feedback category pills
|
|
125
|
+
showOn: ["/dashboard/*", "/app/*"], // route-based visibility
|
|
118
126
|
},
|
|
119
127
|
|
|
120
|
-
// Diagnostics (console + network capture)
|
|
128
|
+
// Diagnostics (console + network capture, including XHR)
|
|
121
129
|
diagnostics: {
|
|
122
130
|
console: true,
|
|
123
|
-
network: true,
|
|
131
|
+
network: true, // captures both fetch and XMLHttpRequest
|
|
124
132
|
},
|
|
125
133
|
|
|
126
134
|
// Screenshot defaults
|
|
@@ -141,6 +149,12 @@ All configuration is passed via the `config` prop on `<BlocFeedWidget>` or `<Blo
|
|
|
141
149
|
backoffMs: 500, // base backoff delay
|
|
142
150
|
},
|
|
143
151
|
|
|
152
|
+
// Security — secret leak detection
|
|
153
|
+
security: {
|
|
154
|
+
secretScan: true,
|
|
155
|
+
scanTargets: ["hydration", "scripts", "meta", "dom"],
|
|
156
|
+
},
|
|
157
|
+
|
|
144
158
|
// Metadata enrichment
|
|
145
159
|
metadata: {
|
|
146
160
|
enabled: true,
|
|
@@ -271,7 +285,103 @@ config={{
|
|
|
271
285
|
}}
|
|
272
286
|
```
|
|
273
287
|
|
|
274
|
-
All fields are optional — only override the ones you need.
|
|
288
|
+
All fields are optional — only override the ones you need. Category labels are also localizable:
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
strings: {
|
|
292
|
+
categoryBug: "Fehler",
|
|
293
|
+
categoryFeature: "Funktion",
|
|
294
|
+
categoryUx: "UX",
|
|
295
|
+
categoryGeneral: "Allgemein",
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Feedback Categories
|
|
300
|
+
|
|
301
|
+
Show a pill selector in the feedback form so users can tag their feedback:
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
config={{
|
|
305
|
+
ui: {
|
|
306
|
+
categories: ["bug", "feature", "ux", "general"],
|
|
307
|
+
},
|
|
308
|
+
}}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
The selected category is included in the payload as `category`. All four categories are shown by default. To show only a subset:
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
config={{
|
|
315
|
+
ui: {
|
|
316
|
+
categories: ["bug", "feature"], // only Bug and Feature pills
|
|
317
|
+
},
|
|
318
|
+
}}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Set `categories: []` to hide the pill selector entirely.
|
|
322
|
+
|
|
323
|
+
## Conditional Display (`showOn`)
|
|
324
|
+
|
|
325
|
+
Restrict widget visibility to specific routes. By default the widget shows on all pages.
|
|
326
|
+
|
|
327
|
+
### Route patterns
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
config={{
|
|
331
|
+
ui: {
|
|
332
|
+
showOn: ["/dashboard/*", "/app/*", "/settings"],
|
|
333
|
+
},
|
|
334
|
+
}}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Patterns support exact matching or wildcard `*` suffix (matches any path starting with the prefix).
|
|
338
|
+
|
|
339
|
+
### Custom predicate
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
config={{
|
|
343
|
+
ui: {
|
|
344
|
+
showOn: (pathname) => !pathname.startsWith("/admin"),
|
|
345
|
+
},
|
|
346
|
+
}}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
The widget automatically detects SPA navigation (`pushState`, `replaceState`, `popstate`) and re-evaluates on every route change.
|
|
350
|
+
|
|
351
|
+
## Programmatic API
|
|
352
|
+
|
|
353
|
+
Control the widget programmatically via a React ref:
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
import { useRef } from "react";
|
|
357
|
+
import { BlocFeedWidget, type BlocFeedHandle } from "blocfeed";
|
|
358
|
+
|
|
359
|
+
function App() {
|
|
360
|
+
const feedbackRef = useRef<BlocFeedHandle>(null);
|
|
361
|
+
|
|
362
|
+
return (
|
|
363
|
+
<>
|
|
364
|
+
<button onClick={() => feedbackRef.current?.open()}>
|
|
365
|
+
Report a Bug
|
|
366
|
+
</button>
|
|
367
|
+
|
|
368
|
+
<BlocFeedWidget
|
|
369
|
+
ref={feedbackRef}
|
|
370
|
+
blocfeed_id="bf_..."
|
|
371
|
+
/>
|
|
372
|
+
</>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### `BlocFeedHandle` methods
|
|
378
|
+
|
|
379
|
+
| Method | Description |
|
|
380
|
+
|--------|-------------|
|
|
381
|
+
| `open()` | Open the widget (starts element picking) |
|
|
382
|
+
| `close()` | Close the widget and reset state |
|
|
383
|
+
| `submit(message)` | Submit feedback programmatically (returns `Promise<SubmitResult>`) |
|
|
384
|
+
| `isOpen` | `boolean` — whether the widget is currently active |
|
|
275
385
|
|
|
276
386
|
## Console & Network Diagnostics
|
|
277
387
|
|
|
@@ -283,7 +393,7 @@ config={{
|
|
|
283
393
|
console: true, // capture console.error & console.warn
|
|
284
394
|
consoleLevels: ["error", "warn"], // which levels to capture
|
|
285
395
|
consoleLimit: 20, // max entries retained
|
|
286
|
-
network: true, // capture failed fetch requests
|
|
396
|
+
network: true, // capture failed fetch & XHR requests
|
|
287
397
|
networkLimit: 15, // max entries retained
|
|
288
398
|
},
|
|
289
399
|
}}
|
|
@@ -291,6 +401,8 @@ config={{
|
|
|
291
401
|
|
|
292
402
|
Captured data is attached to `metadata._consoleLogs` and `metadata._networkErrors` in the payload. BlocFeed's own API calls are excluded. Messages and stacks are truncated at 2KB.
|
|
293
403
|
|
|
404
|
+
Both `fetch` and `XMLHttpRequest` are intercepted, so apps using axios, jQuery, or other XHR-based libraries get full network error capture.
|
|
405
|
+
|
|
294
406
|
### Headless diagnostics
|
|
295
407
|
|
|
296
408
|
```ts
|
|
@@ -302,6 +414,74 @@ import {
|
|
|
302
414
|
} from "blocfeed/engine";
|
|
303
415
|
```
|
|
304
416
|
|
|
417
|
+
## Security — Client-Side Secret Leak Detection
|
|
418
|
+
|
|
419
|
+
Detect accidentally exposed API keys, database credentials, and tokens in client-side code:
|
|
420
|
+
|
|
421
|
+
```tsx
|
|
422
|
+
<BlocFeedWidget
|
|
423
|
+
blocfeed_id="bf_..."
|
|
424
|
+
config={{
|
|
425
|
+
security: {
|
|
426
|
+
secretScan: true, // enable scanning (default when security config is present)
|
|
427
|
+
scanTargets: ["hydration", "scripts", "meta", "dom"], // what to scan
|
|
428
|
+
},
|
|
429
|
+
}}
|
|
430
|
+
/>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
When secrets are detected, a dismissible warning banner appears in the widget, and findings are attached as `metadata._securityFindings` in every feedback submission. Secret values are **never transmitted** — only the first 6 characters + `***`.
|
|
434
|
+
|
|
435
|
+
### What gets scanned
|
|
436
|
+
|
|
437
|
+
| Target | What | Why |
|
|
438
|
+
|--------|------|-----|
|
|
439
|
+
| `hydration` | `__NEXT_DATA__`, `__NUXT__` | Server-side props leaked to client |
|
|
440
|
+
| `scripts` | Inline `<script>` tags | Hardcoded secrets in JavaScript |
|
|
441
|
+
| `meta` | `<meta>` tag `content` attributes | Tokens in meta tags |
|
|
442
|
+
| `dom` | `data-api-key`, `data-secret`, etc. | Keys in HTML attributes |
|
|
443
|
+
|
|
444
|
+
### Built-in patterns (20+)
|
|
445
|
+
|
|
446
|
+
- **Supabase / Postgres**: `SUPABASE_SERVICE_ROLE_KEY`, `POSTGRES_PASSWORD`, `DATABASE_URL` with embedded credentials
|
|
447
|
+
- **AWS**: Access keys (`AKIA...`), secret keys, session tokens
|
|
448
|
+
- **Stripe**: Secret keys (`sk_live_...`, `sk_test_...`), restricted keys
|
|
449
|
+
- **GitHub**: PATs (`ghp_`), OAuth tokens (`gho_`), fine-grained tokens (`github_pat_`)
|
|
450
|
+
- **OpenAI / Anthropic**: API keys
|
|
451
|
+
- **Infrastructure**: Private keys, database URLs with credentials, SendGrid, Twilio
|
|
452
|
+
- **Generic**: Any env var ending in `_SECRET`, `_PASSWORD`, `_PRIVATE_KEY`
|
|
453
|
+
|
|
454
|
+
> **Note**: Known-public keys like `SUPABASE_ANON_KEY` and `SUPABASE_URL` are intentionally **not** flagged.
|
|
455
|
+
|
|
456
|
+
### Custom patterns
|
|
457
|
+
|
|
458
|
+
Add app-specific secret formats:
|
|
459
|
+
|
|
460
|
+
```tsx
|
|
461
|
+
config={{
|
|
462
|
+
security: {
|
|
463
|
+
customPatterns: [
|
|
464
|
+
{ name: "internal_api_key", pattern: /myapp_sk_[a-zA-Z0-9]{32}/ },
|
|
465
|
+
],
|
|
466
|
+
},
|
|
467
|
+
}}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Headless secret scanning
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
import {
|
|
474
|
+
runSecretScan,
|
|
475
|
+
getSecurityFindings,
|
|
476
|
+
clearSecurityFindings,
|
|
477
|
+
} from "blocfeed/engine";
|
|
478
|
+
|
|
479
|
+
runSecretScan({ secretScan: true });
|
|
480
|
+
// ... after scan completes (~100ms)
|
|
481
|
+
const snapshot = getSecurityFindings();
|
|
482
|
+
console.log(snapshot.findings);
|
|
483
|
+
```
|
|
484
|
+
|
|
305
485
|
## Theme Customization
|
|
306
486
|
|
|
307
487
|
Override the widget's visual appearance via CSS variables:
|
|
@@ -314,11 +494,24 @@ config={{
|
|
|
314
494
|
panelBackground: "rgba(0,0,0,0.95)", // panel & trigger background
|
|
315
495
|
panelForeground: "#ffffff", // text color
|
|
316
496
|
fontFamily: "Inter, sans-serif", // font stack
|
|
497
|
+
mode: "auto", // "dark" | "light" | "auto"
|
|
317
498
|
},
|
|
318
499
|
},
|
|
319
500
|
}}
|
|
320
501
|
```
|
|
321
502
|
|
|
503
|
+
### Dark / Light Mode
|
|
504
|
+
|
|
505
|
+
The widget defaults to dark mode. Set `theme.mode` to change the color scheme:
|
|
506
|
+
|
|
507
|
+
| Mode | Behavior |
|
|
508
|
+
|------|----------|
|
|
509
|
+
| `"dark"` | Dark panel, light text (default) |
|
|
510
|
+
| `"light"` | Light panel, dark text |
|
|
511
|
+
| `"auto"` | Follows the user's system preference (`prefers-color-scheme`) and updates live |
|
|
512
|
+
|
|
513
|
+
Explicit `panelBackground` / `panelForeground` overrides take precedence over the mode defaults.
|
|
514
|
+
|
|
322
515
|
## Retry & Transport
|
|
323
516
|
|
|
324
517
|
Configure retry behavior for unreliable networks:
|
|
@@ -489,15 +682,20 @@ All types are exported from both entry points:
|
|
|
489
682
|
```ts
|
|
490
683
|
import type {
|
|
491
684
|
BlocFeedConfig,
|
|
685
|
+
BlocFeedHandle,
|
|
492
686
|
BlocFeedUser,
|
|
493
|
-
|
|
494
|
-
TriggerStyle,
|
|
495
|
-
WidgetPosition,
|
|
496
|
-
ThemeConfig,
|
|
687
|
+
FeedbackCategory,
|
|
497
688
|
FeedbackPayload,
|
|
498
689
|
FeedbackApiResponse,
|
|
499
690
|
BlocFeedState,
|
|
500
691
|
SessionPhase,
|
|
692
|
+
ThemeConfig,
|
|
693
|
+
TransportConfig,
|
|
694
|
+
TriggerStyle,
|
|
695
|
+
WidgetPosition,
|
|
696
|
+
SecurityConfig,
|
|
697
|
+
SecurityFinding,
|
|
698
|
+
SecuritySnapshot,
|
|
501
699
|
// ... and more
|
|
502
700
|
} from "blocfeed";
|
|
503
701
|
```
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';function w(){return typeof window<"u"&&typeof document<"u"}function ue(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 Y(e){return {x:e.x,y:e.y,width:e.width,height:e.height}}function Be(e){return {x:e.x+window.scrollX,y:e.y+window.scrollY,width:e.width,height:e.height}}function De(e){return e.replace(/\s+/g," ").trim()}function Oe(e,t=140){let n=e.textContent;if(!n)return;let r=De(n);if(r)return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}function Ue(e){let t=1;for(let n=e.previousElementSibling;n;n=n.previousElementSibling)n.tagName===e.tagName&&(t+=1);return t}var pe=["data-testid","data-test-id","data-test","data-qa","data-cy"],de="data-blocfeed-component";function He(e){let t=e.closest(`[${de}]`);if(!t)return;let r=t.getAttribute(de)?.trim();return r||void 0}function qe(e){for(let t of pe){let n=e.closest(`[${t}]`);if(!n)continue;let o=n.getAttribute(t)?.trim();if(o)return o}}function fe(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 T(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 I(e){if(e){for(let t of e)if(typeof t.name=="string"&&t.name&&!T(t.name))return t.name}}function k(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 k(o)}}}function $e(e){let t=fe(e);if(!t)return;let n=I(t._debugInfo);if(n)return n;let r=t._debugOwner!==void 0;if(r){let a=t._debugOwner;for(let l=0;a&&l<50;l+=1){let d=I(a._debugInfo);if(d)return d;let g=k(a.type)??k(a.elementType);if(g&&!T(g))return g;a=a._debugOwner;}}if(t._owner!==void 0&&t._owner!==t._debugOwner){let a=t._owner;for(let l=0;a&&l<50;l+=1){let d=I(a._debugInfo);if(d)return d;let g=k(a.type)??k(a.elementType);if(g&&!T(g))return g;a=a._owner;}}let i=t,s=r?80:25;for(let a=0;i&&a<s;a+=1){let l=I(i._debugInfo);if(l)return l;let d=k(i.type)??k(i.elementType);if(d&&!T(d))return d;i=i.return;}let c=e.parentElement;for(let a=0;c&&a<15;a+=1){let l=fe(c);if(l){let d=I(l._debugInfo);if(d)return d;let g=k(l.type)??k(l.elementType);if(g&&!T(g))return g;if(l._debugOwner){let h=k(l._debugOwner.type)??k(l._debugOwner.elementType);if(h&&!T(h))return h}if(l._owner&&l._owner!==l._debugOwner){let h=k(l._owner.type)??k(l._owner.elementType);if(h&&!T(h))return h}}c=c.parentElement;}}function ze(e){let t=e.tagName.toLowerCase(),n=e.getAttribute("id");if(n)return `#${ue(n)}`;for(let r of pe){let o=e.getAttribute(r);if(o)return `${t}[${r}="${ue(o)}"]`}return `${t}:nth-of-type(${Ue(e)})`}function Xe(e,t=10){let n=[],r=e;for(;r&&n.length<t;){let o=ze(r);if(n.unshift(o),o.startsWith("#"))break;r=r.parentElement;}return n.join(" > ")}function me(e,t){if(!t||t.length===0)return false;for(let n of t)if(e.closest(n))return true;return false}function J(e,t){if(!e||me(e,t.ignoreSelectors))return null;let n=e;for(;n;){if(me(n,t.ignoreSelectors))return null;if(t.isSelectable?.(n)??Ze(n))return n;n=n.parentElement;}return null}function Ze(e){let t=e.tagName;return !(t==="HTML"||t==="BODY")}function ge(e){let t=e.getBoundingClientRect(),n={selector:Xe(e),tagName:e.tagName.toLowerCase(),rect:Y(t),pageRect:Be(t)},r=e.getAttribute("id");r&&(n.id=r);let o=e.className;typeof o=="string"&&o.trim()&&(n.className=o);let i=Oe(e);i&&(n.textSnippet=i);let s=He(e)??$e(e);s&&(n.componentName=s);let c=qe(e);return c&&(n.testId=c),n}var G=null;async function he(){return G||(G=import('html-to-image')),G}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 we(e,t){let{width:n,height:r}=await je(e);return {dataUrl:e,mime:t,width:n,height:r}}function L(e){if(e?.aborted)throw new Error("Aborted")}function ye(){return {async captureElement(e,t){if(!w())throw new Error("captureElement can only run in the browser");L(t.signal);let n=await he();L(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 L(t.signal),await we(r,t.mime)},async captureFullPage(e){if(!w())throw new Error("captureFullPage can only run in the browser");L(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)),s=Math.max(1,Math.round(r*o)),c=await he();L(e.signal);let a=e.mime==="image/jpeg"?await c.toJpeg(t,{width:i,height:s,quality:e.quality??.92,pixelRatio:e.pixelRatio}):await c.toPng(t,{width:i,height:s,pixelRatio:e.pixelRatio});return L(e.signal),await we(a,e.mime)}}}function We(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return "Unknown error"}}function y(e,t,n){let r={kind:e,message:We(t)};return n&&(r.detail=n),r}var Ke=12e3,Qe=2048,Ye=.92;function be(){return Date.now()}function Je(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 Ee(e,t,n){let r=new Promise((i,s)=>{let c=setTimeout(()=>s(new Error("Timeout")),t);typeof c.unref=="function"&&c.unref();}),o=[e,r];return n&&o.push(Je(n)),await Promise.race(o)}function Ge(e){if(!w())return 1;let t=window.devicePixelRatio||1,n=e?.pixelRatio??Math.min(t,2);return Math.max(.1,n)}function Ve(e){return !!(e?.element||e?.fullPage)}function _e(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 Se(e){let{selectionElement:t,capture:n,signal:r}=e;if(!w()||!Ve(n))return;let o=be(),i=[],s=n?.timeoutMs??Ke,c=n?.maxDimension??Qe,a=n?.mime??"image/png",l=n?.quality??Ye,d=n?.adapter??ye(),g={},h=Ge(n);if(n?.element&&t)try{let f=t.getBoundingClientRect(),m=Math.min(1,c/Math.max(f.width,f.height)),v=Math.min(h,h*m),A=await Ee(Promise.resolve(d.captureElement(t,{..._e({mime:a,quality:l,pixelRatio:v,maxDimension:c,includeQuality:a==="image/jpeg",...r?{signal:r}:{}})})),s,r);g.element=A;}catch(f){if(r?.aborted)throw f;i.push(y("capture_failed",f,{target:"element"}));}if(n?.fullPage)try{let f=await Ee(Promise.resolve(d.captureFullPage(_e({mime:a,quality:l,pixelRatio:h,maxDimension:c,includeQuality:a==="image/jpeg",...r?{signal:r}:{}}))),s,r);g.fullPage=f;}catch(f){if(r?.aborted)throw f;i.push(y("capture_failed",f,{target:"fullPage"}));}let b=be(),u={startedAt:o,finishedAt:b,durationMs:Math.max(0,b-o)};return i.length>0&&(u.errors=i),{...g,diagnostics:u}}function et(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}function tt(){return w()?{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:et()}:{}}function nt(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 ke(e){let{config:t,context:n,user:r}=e;if(t?.enabled===false)return {};let o={...tt(),...nt(r)},i=t?.enrich;if(!i)return o;try{let s=await i(n);return {...o,...s}}catch(s){let c=y("unknown",s);return {...o,blocfeedMetadataError:c.message}}}var V="blocfeed-queue",rt=50;function ee(){if(!w())return [];try{let e=localStorage.getItem(V);if(!e)return [];let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return []}}function te(e){if(w())try{e.length===0?localStorage.removeItem(V):localStorage.setItem(V,JSON.stringify(e));}catch{}}function ot(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 t}function ne(e){let t=ee(),n=ot(e);for(n.metadata={...n.metadata,_queued:true},t.push({payload:n,timestamp:Date.now()});t.length>rt;)t.shift();te(t);}function ve(){let e=ee();return e.length===0?[]:(te([]),e.map(t=>t.payload))}function Ot(){te([]);}function Ut(){return ee().length}function re(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 X(e){return e instanceof Element?!!e.closest("[data-blocfeed-ui]"):false}function Z(e){e.stopPropagation(),e.stopImmediatePropagation?.();}function Pe(e,t){if(!w())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,s=null,c=(u,f=false)=>{if(!u){i=null,s=null,t.onHover(null);return}let m=Y(u.getBoundingClientRect()),v=`${Math.round(m.x)}:${Math.round(m.y)}:${Math.round(m.width)}:${Math.round(m.height)}`;!f&&u===i&&v===s||(i=u,s=v,t.onHover({element:u,rect:m}));},a=re(u=>{if(X(u.target))return;let f=document.elementFromPoint(u.clientX,u.clientY),m=J(f,o);c(m);}),l=re(()=>{i&&c(i,true);}),d=u=>{X(u.target)||(Z(u),u.pointerType==="mouse"&&u.preventDefault());},g=u=>{X(u.target)||(Z(u),u.pointerType==="mouse"&&u.preventDefault());},h=u=>{if(X(u.target))return;Z(u),u.preventDefault();let f=document.elementFromPoint(u.clientX,u.clientY),m=J(f,o);m&&t.onSelect({element:m,descriptor:ge(m)});},b=u=>{u.key==="Escape"&&(Z(u),u.preventDefault(),t.onCancel());};return window.addEventListener("pointermove",a,{capture:true,passive:true}),window.addEventListener("pointerdown",d,{capture:true}),window.addEventListener("pointerup",g,{capture:true}),window.addEventListener("click",h,{capture:true}),window.addEventListener("keydown",b,{capture:true}),window.addEventListener("scroll",l,{capture:true,passive:true}),window.addEventListener("resize",l,{passive:true}),{stop(){window.removeEventListener("pointermove",a,{capture:true}),window.removeEventListener("pointerdown",d,{capture:true}),window.removeEventListener("pointerup",g,{capture:true}),window.removeEventListener("click",h,{capture:true}),window.removeEventListener("keydown",b,{capture:true}),window.removeEventListener("scroll",l,{capture:true}),window.removeEventListener("resize",l),a.cancel(),l.cancel(),t.onHover(null);}}}var it=12e3,at=2,st=500,ct=2e3,Re="https://blocfeed.com/api/feedback",Ae=0;function xe(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 lt(e){return e>=500&&e<=599}function ut(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),s=new Uint8Array(i.length);for(let c=0;c<i.length;c+=1)s[c]=i.charCodeAt(c);return new Blob([s],{type:o})}function dt(e){let t={},n={...e};if(n.screenshots){let r={},o={...n.screenshots};o.element&&(t.element=o.element.dataUrl,r.element={mime:o.element.mime,width:o.element.width,height:o.element.height},o.element={...o.element,dataUrl:""}),o.fullPage&&(t.fullPage=o.fullPage.dataUrl,r.fullPage={mime:o.fullPage.mime,width:o.fullPage.width,height:o.fullPage.height},o.fullPage={...o.fullPage,dataUrl:""}),n.screenshots=o,(r.element||r.fullPage)&&(n.screenshot_intent=r);}return {lean:n,extracted:t}}async function Te(e,t,n){let r=ut(t);await fetch(e,{method:"PUT",body:r,headers:{"content-type":r.type},...n?{signal:n}:{}});}async function ft(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(`${Re}/${t}/screenshots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(i),...o?{signal:o}:{}});}async function Fe(e){let{signal:t,transport:n}=e;if(Date.now()-Ae<ct)return {ok:false,error:y("configuration",new Error("Please wait before submitting again"))};let o=n?.timeoutMs??it,i=n?.maxAttempts??at,s=n?.backoffMs??st,c=!!(e.payload.screenshots?.element?.dataUrl||e.payload.screenshots?.fullPage?.dataUrl),{lean:a,extracted:l}=c?dt(e.payload):{lean:e.payload,extracted:{}},d={...l,...e.screenshotDataUrls};for(let g=1;g<=i;g+=1){let h=new AbortController,b=setTimeout(()=>h.abort(),o),u=()=>h.abort();t&&t.addEventListener("abort",u,{once:true});try{let f=await fetch(Re,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a),signal:h.signal});if(f.ok){Ae=Date.now();let m;try{m=await f.json();}catch{}if((d.element||d.fullPage)&&m){let p=m.upload_urls;if(p){let _=[];d.element&&p.element&&_.push(Te(p.element,d.element,t)),d.fullPage&&p.fullPage&&_.push(Te(p.fullPage,d.fullPage,t));try{await Promise.all(_);}catch{}}else if(m.feedback_id)try{await ft({feedbackId:m.feedback_id,extracted:d,screenshots:e.payload.screenshots,...t?{signal:t}:{}});}catch{}}let A={ok:!0,status:f.status};return m&&(A.apiResponse=m),A}if(g<i&<(f.status)){let m=.85+Math.random()*.3,v=Math.round(s*2**(g-1)*m);await xe(v,t);continue}return {ok:!1,status:f.status,error:y("api_failed",new Error(`HTTP ${f.status}`))}}catch(f){if(h.signal.aborted||t?.aborted)return {ok:false,error:y("aborted",f)};if(g<i){let m=.85+Math.random()*.3,v=Math.round(s*2**(g-1)*m);await xe(v,t);continue}return {ok:false,error:y("api_failed",f)}}finally{clearTimeout(b),t&&t.removeEventListener("abort",u);}}return {ok:false,error:y("api_failed",new Error("Failed"))}}async function oe(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 Fe(o),r.ok=!!r.api?.ok;}catch(o){r.api={ok:false,error:y("api_failed",o)},r.ok=false;}return {payload:e.payload,result:r}}var mt=["[data-blocfeed-ui]","[data-blocfeed-ignore]"];function pt(e){let t=[...mt,...e?.ignoreSelectors??[]],n=Array.from(new Set(t));return {...e,ignoreSelectors:n}}function Ce(){return {phase:"idle"}}function gt(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=Ce(),r=new Set,o=new Set,i=null,s=null,c=null,a=null,l=0,d=null,g=()=>{for(let p of r)p(n);},h=p=>{for(let _ of o)_(p);},b=p=>{n=p,g();},u=()=>{l+=1,a?.abort(),a=null;},f=()=>{i?.stop(),i=null,h(null),c!==null&&w()&&(document.documentElement.style.cursor=c,c=null);},m=()=>{u(),f(),s=null,b(Ce());},v=()=>{if(!w())return;f(),s=null;let p=pt(t.picker);c=document.documentElement.style.cursor,document.documentElement.style.cursor="crosshair",b({phase:"picking"}),i=Pe(p,{onHover:h,onSelect:({element:_,descriptor:z})=>{s=_,f(),b({phase:"review",selection:z});},onCancel:()=>{m();}});},A=()=>{let p=ve();if(p.length!==0)for(let _ of p)oe({payload:_,...t.transport?{transport:t.transport}:{}}).catch(()=>{ne(_);});};if(w()){setTimeout(A,1e3);let p=()=>A();window.addEventListener("online",p),d=()=>window.removeEventListener("online",p);}return {getState:()=>n,subscribe(p){return r.add(p),()=>r.delete(p)},subscribeHover(p){return o.add(p),()=>o.delete(p)},start(){n.phase==="capturing"||n.phase==="submitting"||n.phase!=="picking"&&v();},stop(){m();},clearSelection(){n.phase==="capturing"||n.phase==="submitting"||v();},setConfig(p){t=p;},async submit(p,_){if(!w()){let E=y("configuration",new Error("BlocFeed submit can only run in the browser"));return b({phase:"error",lastError:E}),{ok:false}}let z=t.blocfeed_id?.trim?.()??"";if(!z){let x={phase:"error",lastError:y("configuration",new Error("Missing blocfeed_id. Create a project in BlocFeed and pass its blocfeed_id."))};return n.selection&&(x.selection=n.selection),b(x),{ok:false}}if(n.phase==="capturing"||n.phase==="submitting")return {ok:false};let N=l+1;l=N,a?.abort(),a=new AbortController;let F=a.signal,S=n.selection,W=_?.capture?{...t.capture,..._.capture}:t.capture,ce=!!(W?.element||W?.fullPage),le={phase:ce?"capturing":"submitting"};S&&(le.selection=S),b(le);try{let E=ce?await Se({selectionElement:s,capture:W,signal:F}):void 0;if(F.aborted||l!==N)return {ok:!1};let x={phase:"submitting"};S&&(x.selection=S),E&&(x.capture=E),b(x);let C={};S&&(C.selection=S),E&&(C.capture=E);let Ne=await ke({config:t.metadata,context:C,...t.user?{user:t.user}:{}}),M={version:1,createdAt:new Date().toISOString(),blocfeed_id:z,message:p,metadata:Ne};_?.category&&(M.category=_.category),t.user&&(M.user=t.user),S&&(M.selection=S),E&&(M.screenshots=E);let{result:P}=await oe({payload:M,signal:F,...t.transport?{transport:t.transport}:{}});if(F.aborted||l!==N)return P;if(P.ok){let Q={phase:"success",lastSubmit:P};return S&&(Q.selection=S),E&&(Q.capture=E),b(Q),P}gt(P)&&ne(M);let Ie=P.api?.error??y("unknown",new Error("Submission failed")),K={phase:"error",lastSubmit:P,lastError:Ie};return S&&(K.selection=S),E&&(K.capture=E),b(K),P}catch(E){if(F.aborted||l!==N)return {ok:false};let C={phase:"error",lastError:F.aborted?y("aborted",E):y("unknown",E)};return S&&(C.selection=S),b(C),{ok:false}}finally{l===N&&(a=null);}},__unsafeGetSelectedElement(){return s},destroy(){m(),r.clear(),o.clear(),d?.(),d=null;}}}var B=[],D=[],Me=20,Le=15,O={},U=null,H=null,q=null,j=false;function ht(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function wt(e,t){let n=t.map(ht).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)),B.push(o);B.length>Me;)B.shift();}function ie(e){if(!e.url.includes("blocfeed.com"))for(D.push(e);D.length>Le;)D.shift();}function sn(e={}){if(!(j||!w())){if(j=true,Me=e.consoleLimit??20,Le=e.networkLimit??15,e.console!==false){let t=e.consoleLevels??["error","warn"];for(let n of t)O[n]=console[n],console[n]=(...r)=>{wt(n,r),O[n]?.apply(console,r);};}if(e.network!==false&&typeof window.fetch=="function"){U=window.fetch;let t=U;window.fetch=async function(r,o){let i=typeof r=="string"?r:r instanceof URL?r.toString():r.url,s=(o?.method??"GET").toUpperCase(),c=Date.now();try{let a=await t.call(window,r,o);return a.ok||ie({url:i.slice(0,500),method:s,status:a.status,statusText:a.statusText,timestamp:c,durationMs:Date.now()-c}),a}catch(a){throw ie({url:i.slice(0,500),method:s,status:0,statusText:a instanceof Error?a.message:"Network error",timestamp:c,durationMs:Date.now()-c}),a}};}if(e.network!==false&&typeof XMLHttpRequest<"u"){H=XMLHttpRequest.prototype.open,q=XMLHttpRequest.prototype.send;let t=H,n=q;XMLHttpRequest.prototype.open=function(r,o,...i){return this.__bf_method=r.toUpperCase(),this.__bf_url=String(o),t.apply(this,[r,o,...i])},XMLHttpRequest.prototype.send=function(...r){let o=this.__bf_method||"GET",i=this.__bf_url||"",s=Date.now();return this.addEventListener("loadend",function(){if(this.status>=400||this.status===0){let c={url:i.slice(0,500),method:o,status:this.status,timestamp:s,durationMs:Date.now()-s};this.statusText&&(c.statusText=this.statusText),ie(c);}}),n.apply(this,r)};}}}function cn(){if(j){for(let[e,t]of Object.entries(O))console[e]=t;for(let e of Object.keys(O))delete O[e];U&&(window.fetch=U,U=null),H&&(XMLHttpRequest.prototype.open=H,H=null),q&&(XMLHttpRequest.prototype.send=q,q=null),j=false;}}function ln(){return {consoleLogs:[...B],networkErrors:[...D]}}function un(){B=[],D=[];}var yt=[{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,}/}],R=[],se=0,ae=false;function bt(e){let t=e.slice(0,80);return t.length<=6?"***":t.slice(0,6)+"***"}function Et(e,t,n,r){if(R.some(s=>s.rule===e&&s.source===t))return;let i={rule:e,source:t,hint:bt(n),timestamp:Date.now()};r&&(i.location=r),R.push(i);}function $(e,t,n,r){for(let{name:o,pattern:i}of n){let s=i.exec(e);s&&Et(o,t,s[0],r);}}function _t(e){try{let t=window;if(t.__NEXT_DATA__){let n=JSON.stringify(t.__NEXT_DATA__);$(n,"hydration",e,"__NEXT_DATA__");}if(t.__NUXT__){let n=JSON.stringify(t.__NUXT__);$(n,"hydration",e,"__NUXT__");}}catch{}}function St(e){try{document.querySelectorAll("script:not([src])").forEach((n,r)=>{let o=n.textContent;o&&o.length>0&&$(o,"scripts",e,`inline-script[${r}]`);});}catch{}}function kt(e){try{document.querySelectorAll("meta[content]").forEach(n=>{let r=n.getAttribute("content"),o=n.getAttribute("name")||n.getAttribute("property")||"";r&&r.length>10&&$(r,"meta",e,o?`meta[${o}]`:void 0);});}catch{}}function vt(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&&$(i.value,"dom",e,`${n.tagName.toLowerCase()}[${i.name}]`);}});}catch{}}function mn(e={}){if(ae||!w()||(ae=true,e.secretScan===false))return;let t=[...yt,...e.customPatterns??[]],n=e.scanTargets??["hydration","scripts","meta","dom"];setTimeout(()=>{se=Date.now(),n.includes("hydration")&&_t(t),n.includes("scripts")&&St(t),n.includes("meta")&&kt(t),n.includes("dom")&&vt(t);let r=e.notify??"both";R.length>0&&(r==="console"||r==="both")&&console.warn(`[BlocFeed Security] Found ${R.length} potential secret(s) exposed in client code:`,R.map(o=>`${o.rule} (${o.source}${o.location?`: ${o.location}`:""})`));},100);}function pn(){return {findings:[...R],scannedAt:se}}function gn(){R=[],se=0,ae=false;}
|
|
2
|
+
exports.a=w;exports.b=Y;exports.c=ye;exports.d=Se;exports.e=ke;exports.f=ne;exports.g=ve;exports.h=Ot;exports.i=Ut;exports.j=rn;exports.k=sn;exports.l=cn;exports.m=ln;exports.n=un;exports.o=mn;exports.p=pn;exports.q=gn;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function w(){return typeof window<"u"&&typeof document<"u"}function ue(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 Y(e){return {x:e.x,y:e.y,width:e.width,height:e.height}}function Be(e){return {x:e.x+window.scrollX,y:e.y+window.scrollY,width:e.width,height:e.height}}function De(e){return e.replace(/\s+/g," ").trim()}function Oe(e,t=140){let n=e.textContent;if(!n)return;let r=De(n);if(r)return r.length<=t?r:`${r.slice(0,t-1)}\u2026`}function Ue(e){let t=1;for(let n=e.previousElementSibling;n;n=n.previousElementSibling)n.tagName===e.tagName&&(t+=1);return t}var pe=["data-testid","data-test-id","data-test","data-qa","data-cy"],de="data-blocfeed-component";function He(e){let t=e.closest(`[${de}]`);if(!t)return;let r=t.getAttribute(de)?.trim();return r||void 0}function qe(e){for(let t of pe){let n=e.closest(`[${t}]`);if(!n)continue;let o=n.getAttribute(t)?.trim();if(o)return o}}function fe(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 T(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 I(e){if(e){for(let t of e)if(typeof t.name=="string"&&t.name&&!T(t.name))return t.name}}function k(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 k(o)}}}function $e(e){let t=fe(e);if(!t)return;let n=I(t._debugInfo);if(n)return n;let r=t._debugOwner!==void 0;if(r){let a=t._debugOwner;for(let l=0;a&&l<50;l+=1){let d=I(a._debugInfo);if(d)return d;let g=k(a.type)??k(a.elementType);if(g&&!T(g))return g;a=a._debugOwner;}}if(t._owner!==void 0&&t._owner!==t._debugOwner){let a=t._owner;for(let l=0;a&&l<50;l+=1){let d=I(a._debugInfo);if(d)return d;let g=k(a.type)??k(a.elementType);if(g&&!T(g))return g;a=a._owner;}}let i=t,s=r?80:25;for(let a=0;i&&a<s;a+=1){let l=I(i._debugInfo);if(l)return l;let d=k(i.type)??k(i.elementType);if(d&&!T(d))return d;i=i.return;}let c=e.parentElement;for(let a=0;c&&a<15;a+=1){let l=fe(c);if(l){let d=I(l._debugInfo);if(d)return d;let g=k(l.type)??k(l.elementType);if(g&&!T(g))return g;if(l._debugOwner){let h=k(l._debugOwner.type)??k(l._debugOwner.elementType);if(h&&!T(h))return h}if(l._owner&&l._owner!==l._debugOwner){let h=k(l._owner.type)??k(l._owner.elementType);if(h&&!T(h))return h}}c=c.parentElement;}}function ze(e){let t=e.tagName.toLowerCase(),n=e.getAttribute("id");if(n)return `#${ue(n)}`;for(let r of pe){let o=e.getAttribute(r);if(o)return `${t}[${r}="${ue(o)}"]`}return `${t}:nth-of-type(${Ue(e)})`}function Xe(e,t=10){let n=[],r=e;for(;r&&n.length<t;){let o=ze(r);if(n.unshift(o),o.startsWith("#"))break;r=r.parentElement;}return n.join(" > ")}function me(e,t){if(!t||t.length===0)return false;for(let n of t)if(e.closest(n))return true;return false}function J(e,t){if(!e||me(e,t.ignoreSelectors))return null;let n=e;for(;n;){if(me(n,t.ignoreSelectors))return null;if(t.isSelectable?.(n)??Ze(n))return n;n=n.parentElement;}return null}function Ze(e){let t=e.tagName;return !(t==="HTML"||t==="BODY")}function ge(e){let t=e.getBoundingClientRect(),n={selector:Xe(e),tagName:e.tagName.toLowerCase(),rect:Y(t),pageRect:Be(t)},r=e.getAttribute("id");r&&(n.id=r);let o=e.className;typeof o=="string"&&o.trim()&&(n.className=o);let i=Oe(e);i&&(n.textSnippet=i);let s=He(e)??$e(e);s&&(n.componentName=s);let c=qe(e);return c&&(n.testId=c),n}var G=null;async function he(){return G||(G=import('html-to-image')),G}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 we(e,t){let{width:n,height:r}=await je(e);return {dataUrl:e,mime:t,width:n,height:r}}function L(e){if(e?.aborted)throw new Error("Aborted")}function ye(){return {async captureElement(e,t){if(!w())throw new Error("captureElement can only run in the browser");L(t.signal);let n=await he();L(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 L(t.signal),await we(r,t.mime)},async captureFullPage(e){if(!w())throw new Error("captureFullPage can only run in the browser");L(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)),s=Math.max(1,Math.round(r*o)),c=await he();L(e.signal);let a=e.mime==="image/jpeg"?await c.toJpeg(t,{width:i,height:s,quality:e.quality??.92,pixelRatio:e.pixelRatio}):await c.toPng(t,{width:i,height:s,pixelRatio:e.pixelRatio});return L(e.signal),await we(a,e.mime)}}}function We(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return "Unknown error"}}function y(e,t,n){let r={kind:e,message:We(t)};return n&&(r.detail=n),r}var Ke=12e3,Qe=2048,Ye=.92;function be(){return Date.now()}function Je(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 Ee(e,t,n){let r=new Promise((i,s)=>{let c=setTimeout(()=>s(new Error("Timeout")),t);typeof c.unref=="function"&&c.unref();}),o=[e,r];return n&&o.push(Je(n)),await Promise.race(o)}function Ge(e){if(!w())return 1;let t=window.devicePixelRatio||1,n=e?.pixelRatio??Math.min(t,2);return Math.max(.1,n)}function Ve(e){return !!(e?.element||e?.fullPage)}function _e(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 Se(e){let{selectionElement:t,capture:n,signal:r}=e;if(!w()||!Ve(n))return;let o=be(),i=[],s=n?.timeoutMs??Ke,c=n?.maxDimension??Qe,a=n?.mime??"image/png",l=n?.quality??Ye,d=n?.adapter??ye(),g={},h=Ge(n);if(n?.element&&t)try{let f=t.getBoundingClientRect(),m=Math.min(1,c/Math.max(f.width,f.height)),v=Math.min(h,h*m),A=await Ee(Promise.resolve(d.captureElement(t,{..._e({mime:a,quality:l,pixelRatio:v,maxDimension:c,includeQuality:a==="image/jpeg",...r?{signal:r}:{}})})),s,r);g.element=A;}catch(f){if(r?.aborted)throw f;i.push(y("capture_failed",f,{target:"element"}));}if(n?.fullPage)try{let f=await Ee(Promise.resolve(d.captureFullPage(_e({mime:a,quality:l,pixelRatio:h,maxDimension:c,includeQuality:a==="image/jpeg",...r?{signal:r}:{}}))),s,r);g.fullPage=f;}catch(f){if(r?.aborted)throw f;i.push(y("capture_failed",f,{target:"fullPage"}));}let b=be(),u={startedAt:o,finishedAt:b,durationMs:Math.max(0,b-o)};return i.length>0&&(u.errors=i),{...g,diagnostics:u}}function et(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return}}function tt(){return w()?{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:et()}:{}}function nt(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 ke(e){let{config:t,context:n,user:r}=e;if(t?.enabled===false)return {};let o={...tt(),...nt(r)},i=t?.enrich;if(!i)return o;try{let s=await i(n);return {...o,...s}}catch(s){let c=y("unknown",s);return {...o,blocfeedMetadataError:c.message}}}var V="blocfeed-queue",rt=50;function ee(){if(!w())return [];try{let e=localStorage.getItem(V);if(!e)return [];let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return []}}function te(e){if(w())try{e.length===0?localStorage.removeItem(V):localStorage.setItem(V,JSON.stringify(e));}catch{}}function ot(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 t}function ne(e){let t=ee(),n=ot(e);for(n.metadata={...n.metadata,_queued:true},t.push({payload:n,timestamp:Date.now()});t.length>rt;)t.shift();te(t);}function ve(){let e=ee();return e.length===0?[]:(te([]),e.map(t=>t.payload))}function Ot(){te([]);}function Ut(){return ee().length}function re(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 X(e){return e instanceof Element?!!e.closest("[data-blocfeed-ui]"):false}function Z(e){e.stopPropagation(),e.stopImmediatePropagation?.();}function Pe(e,t){if(!w())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,s=null,c=(u,f=false)=>{if(!u){i=null,s=null,t.onHover(null);return}let m=Y(u.getBoundingClientRect()),v=`${Math.round(m.x)}:${Math.round(m.y)}:${Math.round(m.width)}:${Math.round(m.height)}`;!f&&u===i&&v===s||(i=u,s=v,t.onHover({element:u,rect:m}));},a=re(u=>{if(X(u.target))return;let f=document.elementFromPoint(u.clientX,u.clientY),m=J(f,o);c(m);}),l=re(()=>{i&&c(i,true);}),d=u=>{X(u.target)||(Z(u),u.pointerType==="mouse"&&u.preventDefault());},g=u=>{X(u.target)||(Z(u),u.pointerType==="mouse"&&u.preventDefault());},h=u=>{if(X(u.target))return;Z(u),u.preventDefault();let f=document.elementFromPoint(u.clientX,u.clientY),m=J(f,o);m&&t.onSelect({element:m,descriptor:ge(m)});},b=u=>{u.key==="Escape"&&(Z(u),u.preventDefault(),t.onCancel());};return window.addEventListener("pointermove",a,{capture:true,passive:true}),window.addEventListener("pointerdown",d,{capture:true}),window.addEventListener("pointerup",g,{capture:true}),window.addEventListener("click",h,{capture:true}),window.addEventListener("keydown",b,{capture:true}),window.addEventListener("scroll",l,{capture:true,passive:true}),window.addEventListener("resize",l,{passive:true}),{stop(){window.removeEventListener("pointermove",a,{capture:true}),window.removeEventListener("pointerdown",d,{capture:true}),window.removeEventListener("pointerup",g,{capture:true}),window.removeEventListener("click",h,{capture:true}),window.removeEventListener("keydown",b,{capture:true}),window.removeEventListener("scroll",l,{capture:true}),window.removeEventListener("resize",l),a.cancel(),l.cancel(),t.onHover(null);}}}var it=12e3,at=2,st=500,ct=2e3,Re="https://blocfeed.com/api/feedback",Ae=0;function xe(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 lt(e){return e>=500&&e<=599}function ut(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),s=new Uint8Array(i.length);for(let c=0;c<i.length;c+=1)s[c]=i.charCodeAt(c);return new Blob([s],{type:o})}function dt(e){let t={},n={...e};if(n.screenshots){let r={},o={...n.screenshots};o.element&&(t.element=o.element.dataUrl,r.element={mime:o.element.mime,width:o.element.width,height:o.element.height},o.element={...o.element,dataUrl:""}),o.fullPage&&(t.fullPage=o.fullPage.dataUrl,r.fullPage={mime:o.fullPage.mime,width:o.fullPage.width,height:o.fullPage.height},o.fullPage={...o.fullPage,dataUrl:""}),n.screenshots=o,(r.element||r.fullPage)&&(n.screenshot_intent=r);}return {lean:n,extracted:t}}async function Te(e,t,n){let r=ut(t);await fetch(e,{method:"PUT",body:r,headers:{"content-type":r.type},...n?{signal:n}:{}});}async function ft(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(`${Re}/${t}/screenshots`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(i),...o?{signal:o}:{}});}async function Fe(e){let{signal:t,transport:n}=e;if(Date.now()-Ae<ct)return {ok:false,error:y("configuration",new Error("Please wait before submitting again"))};let o=n?.timeoutMs??it,i=n?.maxAttempts??at,s=n?.backoffMs??st,c=!!(e.payload.screenshots?.element?.dataUrl||e.payload.screenshots?.fullPage?.dataUrl),{lean:a,extracted:l}=c?dt(e.payload):{lean:e.payload,extracted:{}},d={...l,...e.screenshotDataUrls};for(let g=1;g<=i;g+=1){let h=new AbortController,b=setTimeout(()=>h.abort(),o),u=()=>h.abort();t&&t.addEventListener("abort",u,{once:true});try{let f=await fetch(Re,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a),signal:h.signal});if(f.ok){Ae=Date.now();let m;try{m=await f.json();}catch{}if((d.element||d.fullPage)&&m){let p=m.upload_urls;if(p){let _=[];d.element&&p.element&&_.push(Te(p.element,d.element,t)),d.fullPage&&p.fullPage&&_.push(Te(p.fullPage,d.fullPage,t));try{await Promise.all(_);}catch{}}else if(m.feedback_id)try{await ft({feedbackId:m.feedback_id,extracted:d,screenshots:e.payload.screenshots,...t?{signal:t}:{}});}catch{}}let A={ok:!0,status:f.status};return m&&(A.apiResponse=m),A}if(g<i&<(f.status)){let m=.85+Math.random()*.3,v=Math.round(s*2**(g-1)*m);await xe(v,t);continue}return {ok:!1,status:f.status,error:y("api_failed",new Error(`HTTP ${f.status}`))}}catch(f){if(h.signal.aborted||t?.aborted)return {ok:false,error:y("aborted",f)};if(g<i){let m=.85+Math.random()*.3,v=Math.round(s*2**(g-1)*m);await xe(v,t);continue}return {ok:false,error:y("api_failed",f)}}finally{clearTimeout(b),t&&t.removeEventListener("abort",u);}}return {ok:false,error:y("api_failed",new Error("Failed"))}}async function oe(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 Fe(o),r.ok=!!r.api?.ok;}catch(o){r.api={ok:false,error:y("api_failed",o)},r.ok=false;}return {payload:e.payload,result:r}}var mt=["[data-blocfeed-ui]","[data-blocfeed-ignore]"];function pt(e){let t=[...mt,...e?.ignoreSelectors??[]],n=Array.from(new Set(t));return {...e,ignoreSelectors:n}}function Ce(){return {phase:"idle"}}function gt(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=Ce(),r=new Set,o=new Set,i=null,s=null,c=null,a=null,l=0,d=null,g=()=>{for(let p of r)p(n);},h=p=>{for(let _ of o)_(p);},b=p=>{n=p,g();},u=()=>{l+=1,a?.abort(),a=null;},f=()=>{i?.stop(),i=null,h(null),c!==null&&w()&&(document.documentElement.style.cursor=c,c=null);},m=()=>{u(),f(),s=null,b(Ce());},v=()=>{if(!w())return;f(),s=null;let p=pt(t.picker);c=document.documentElement.style.cursor,document.documentElement.style.cursor="crosshair",b({phase:"picking"}),i=Pe(p,{onHover:h,onSelect:({element:_,descriptor:z})=>{s=_,f(),b({phase:"review",selection:z});},onCancel:()=>{m();}});},A=()=>{let p=ve();if(p.length!==0)for(let _ of p)oe({payload:_,...t.transport?{transport:t.transport}:{}}).catch(()=>{ne(_);});};if(w()){setTimeout(A,1e3);let p=()=>A();window.addEventListener("online",p),d=()=>window.removeEventListener("online",p);}return {getState:()=>n,subscribe(p){return r.add(p),()=>r.delete(p)},subscribeHover(p){return o.add(p),()=>o.delete(p)},start(){n.phase==="capturing"||n.phase==="submitting"||n.phase!=="picking"&&v();},stop(){m();},clearSelection(){n.phase==="capturing"||n.phase==="submitting"||v();},setConfig(p){t=p;},async submit(p,_){if(!w()){let E=y("configuration",new Error("BlocFeed submit can only run in the browser"));return b({phase:"error",lastError:E}),{ok:false}}let z=t.blocfeed_id?.trim?.()??"";if(!z){let x={phase:"error",lastError:y("configuration",new Error("Missing blocfeed_id. Create a project in BlocFeed and pass its blocfeed_id."))};return n.selection&&(x.selection=n.selection),b(x),{ok:false}}if(n.phase==="capturing"||n.phase==="submitting")return {ok:false};let N=l+1;l=N,a?.abort(),a=new AbortController;let F=a.signal,S=n.selection,W=_?.capture?{...t.capture,..._.capture}:t.capture,ce=!!(W?.element||W?.fullPage),le={phase:ce?"capturing":"submitting"};S&&(le.selection=S),b(le);try{let E=ce?await Se({selectionElement:s,capture:W,signal:F}):void 0;if(F.aborted||l!==N)return {ok:!1};let x={phase:"submitting"};S&&(x.selection=S),E&&(x.capture=E),b(x);let C={};S&&(C.selection=S),E&&(C.capture=E);let Ne=await ke({config:t.metadata,context:C,...t.user?{user:t.user}:{}}),M={version:1,createdAt:new Date().toISOString(),blocfeed_id:z,message:p,metadata:Ne};_?.category&&(M.category=_.category),t.user&&(M.user=t.user),S&&(M.selection=S),E&&(M.screenshots=E);let{result:P}=await oe({payload:M,signal:F,...t.transport?{transport:t.transport}:{}});if(F.aborted||l!==N)return P;if(P.ok){let Q={phase:"success",lastSubmit:P};return S&&(Q.selection=S),E&&(Q.capture=E),b(Q),P}gt(P)&&ne(M);let Ie=P.api?.error??y("unknown",new Error("Submission failed")),K={phase:"error",lastSubmit:P,lastError:Ie};return S&&(K.selection=S),E&&(K.capture=E),b(K),P}catch(E){if(F.aborted||l!==N)return {ok:false};let C={phase:"error",lastError:F.aborted?y("aborted",E):y("unknown",E)};return S&&(C.selection=S),b(C),{ok:false}}finally{l===N&&(a=null);}},__unsafeGetSelectedElement(){return s},destroy(){m(),r.clear(),o.clear(),d?.(),d=null;}}}var B=[],D=[],Me=20,Le=15,O={},U=null,H=null,q=null,j=false;function ht(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}function wt(e,t){let n=t.map(ht).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)),B.push(o);B.length>Me;)B.shift();}function ie(e){if(!e.url.includes("blocfeed.com"))for(D.push(e);D.length>Le;)D.shift();}function sn(e={}){if(!(j||!w())){if(j=true,Me=e.consoleLimit??20,Le=e.networkLimit??15,e.console!==false){let t=e.consoleLevels??["error","warn"];for(let n of t)O[n]=console[n],console[n]=(...r)=>{wt(n,r),O[n]?.apply(console,r);};}if(e.network!==false&&typeof window.fetch=="function"){U=window.fetch;let t=U;window.fetch=async function(r,o){let i=typeof r=="string"?r:r instanceof URL?r.toString():r.url,s=(o?.method??"GET").toUpperCase(),c=Date.now();try{let a=await t.call(window,r,o);return a.ok||ie({url:i.slice(0,500),method:s,status:a.status,statusText:a.statusText,timestamp:c,durationMs:Date.now()-c}),a}catch(a){throw ie({url:i.slice(0,500),method:s,status:0,statusText:a instanceof Error?a.message:"Network error",timestamp:c,durationMs:Date.now()-c}),a}};}if(e.network!==false&&typeof XMLHttpRequest<"u"){H=XMLHttpRequest.prototype.open,q=XMLHttpRequest.prototype.send;let t=H,n=q;XMLHttpRequest.prototype.open=function(r,o,...i){return this.__bf_method=r.toUpperCase(),this.__bf_url=String(o),t.apply(this,[r,o,...i])},XMLHttpRequest.prototype.send=function(...r){let o=this.__bf_method||"GET",i=this.__bf_url||"",s=Date.now();return this.addEventListener("loadend",function(){if(this.status>=400||this.status===0){let c={url:i.slice(0,500),method:o,status:this.status,timestamp:s,durationMs:Date.now()-s};this.statusText&&(c.statusText=this.statusText),ie(c);}}),n.apply(this,r)};}}}function cn(){if(j){for(let[e,t]of Object.entries(O))console[e]=t;for(let e of Object.keys(O))delete O[e];U&&(window.fetch=U,U=null),H&&(XMLHttpRequest.prototype.open=H,H=null),q&&(XMLHttpRequest.prototype.send=q,q=null),j=false;}}function ln(){return {consoleLogs:[...B],networkErrors:[...D]}}function un(){B=[],D=[];}var yt=[{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,}/}],R=[],se=0,ae=false;function bt(e){let t=e.slice(0,80);return t.length<=6?"***":t.slice(0,6)+"***"}function Et(e,t,n,r){if(R.some(s=>s.rule===e&&s.source===t))return;let i={rule:e,source:t,hint:bt(n),timestamp:Date.now()};r&&(i.location=r),R.push(i);}function $(e,t,n,r){for(let{name:o,pattern:i}of n){let s=i.exec(e);s&&Et(o,t,s[0],r);}}function _t(e){try{let t=window;if(t.__NEXT_DATA__){let n=JSON.stringify(t.__NEXT_DATA__);$(n,"hydration",e,"__NEXT_DATA__");}if(t.__NUXT__){let n=JSON.stringify(t.__NUXT__);$(n,"hydration",e,"__NUXT__");}}catch{}}function St(e){try{document.querySelectorAll("script:not([src])").forEach((n,r)=>{let o=n.textContent;o&&o.length>0&&$(o,"scripts",e,`inline-script[${r}]`);});}catch{}}function kt(e){try{document.querySelectorAll("meta[content]").forEach(n=>{let r=n.getAttribute("content"),o=n.getAttribute("name")||n.getAttribute("property")||"";r&&r.length>10&&$(r,"meta",e,o?`meta[${o}]`:void 0);});}catch{}}function vt(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&&$(i.value,"dom",e,`${n.tagName.toLowerCase()}[${i.name}]`);}});}catch{}}function mn(e={}){if(ae||!w()||(ae=true,e.secretScan===false))return;let t=[...yt,...e.customPatterns??[]],n=e.scanTargets??["hydration","scripts","meta","dom"];setTimeout(()=>{se=Date.now(),n.includes("hydration")&&_t(t),n.includes("scripts")&&St(t),n.includes("meta")&&kt(t),n.includes("dom")&&vt(t);let r=e.notify??"both";R.length>0&&(r==="console"||r==="both")&&console.warn(`[BlocFeed Security] Found ${R.length} potential secret(s) exposed in client code:`,R.map(o=>`${o.rule} (${o.source}${o.location?`: ${o.location}`:""})`));},100);}function pn(){return {findings:[...R],scannedAt:se}}function gn(){R=[],se=0,ae=false;}
|
|
2
|
+
export{w as a,Y as b,ye as c,Se as d,ke as e,ne as f,ve as g,Ot as h,Ut as i,rn as j,sn as k,cn as l,ln as m,un as n,mn as o,pn as p,gn as q};
|
|
@@ -30,6 +30,15 @@ interface ThemeConfig {
|
|
|
30
30
|
panelBackground?: string;
|
|
31
31
|
panelForeground?: string;
|
|
32
32
|
fontFamily?: string;
|
|
33
|
+
/** Color mode. Default: "dark" */
|
|
34
|
+
mode?: "light" | "dark" | "auto";
|
|
35
|
+
}
|
|
36
|
+
type FeedbackCategory = "bug" | "feature" | "ux" | "general";
|
|
37
|
+
interface BlocFeedHandle {
|
|
38
|
+
open: () => void;
|
|
39
|
+
close: () => void;
|
|
40
|
+
submit: (message: string) => Promise<SubmitResult>;
|
|
41
|
+
isOpen: boolean;
|
|
33
42
|
}
|
|
34
43
|
interface BlocFeedStrings {
|
|
35
44
|
triggerLabel?: string;
|
|
@@ -46,6 +55,10 @@ interface BlocFeedStrings {
|
|
|
46
55
|
successText?: string;
|
|
47
56
|
toastText?: string;
|
|
48
57
|
sendButton?: string;
|
|
58
|
+
categoryBug?: string;
|
|
59
|
+
categoryFeature?: string;
|
|
60
|
+
categoryUx?: string;
|
|
61
|
+
categoryGeneral?: string;
|
|
49
62
|
}
|
|
50
63
|
interface ConsoleEntry {
|
|
51
64
|
level: "error" | "warn" | "log";
|
|
@@ -73,6 +86,36 @@ interface DiagnosticsConfig {
|
|
|
73
86
|
/** Max network entries to retain. Default: 15 */
|
|
74
87
|
networkLimit?: number;
|
|
75
88
|
}
|
|
89
|
+
type SecurityScanTarget = "hydration" | "scripts" | "meta" | "dom";
|
|
90
|
+
interface SecurityConfig {
|
|
91
|
+
/** Enable secret leak scanning. Default: true when security config is present. */
|
|
92
|
+
secretScan?: boolean;
|
|
93
|
+
/** Additional regex patterns to scan for. */
|
|
94
|
+
customPatterns?: Array<{
|
|
95
|
+
name: string;
|
|
96
|
+
pattern: RegExp;
|
|
97
|
+
}>;
|
|
98
|
+
/** What to scan. Default: all targets. */
|
|
99
|
+
scanTargets?: SecurityScanTarget[];
|
|
100
|
+
/** Where to report findings. Default: "both" */
|
|
101
|
+
notify?: "dashboard" | "console" | "both";
|
|
102
|
+
}
|
|
103
|
+
interface SecurityFinding {
|
|
104
|
+
/** Which pattern matched. */
|
|
105
|
+
rule: string;
|
|
106
|
+
/** Where it was found. */
|
|
107
|
+
source: SecurityScanTarget;
|
|
108
|
+
/** Truncated/redacted hint (first 6 chars + "***") — NEVER the full secret. */
|
|
109
|
+
hint: string;
|
|
110
|
+
/** Location details (e.g. script index, meta name). */
|
|
111
|
+
location?: string;
|
|
112
|
+
/** Timestamp. */
|
|
113
|
+
timestamp: number;
|
|
114
|
+
}
|
|
115
|
+
interface SecuritySnapshot {
|
|
116
|
+
findings: SecurityFinding[];
|
|
117
|
+
scannedAt: number;
|
|
118
|
+
}
|
|
76
119
|
interface Rect {
|
|
77
120
|
x: number;
|
|
78
121
|
y: number;
|
|
@@ -190,6 +233,8 @@ interface BlocFeedConfig {
|
|
|
190
233
|
transport?: TransportConfig;
|
|
191
234
|
/** Diagnostics capture (console logs + failed network requests). */
|
|
192
235
|
diagnostics?: DiagnosticsConfig;
|
|
236
|
+
/** Client-side secret leak detection. */
|
|
237
|
+
security?: SecurityConfig;
|
|
193
238
|
ui?: {
|
|
194
239
|
/** z-index for the widget overlay/panel. */
|
|
195
240
|
zIndex?: number;
|
|
@@ -207,6 +252,15 @@ interface BlocFeedConfig {
|
|
|
207
252
|
strings?: BlocFeedStrings;
|
|
208
253
|
/** Show "Powered by BlocFeed" watermark. Default: true */
|
|
209
254
|
branding?: boolean;
|
|
255
|
+
/**
|
|
256
|
+
* Restrict widget visibility to specific routes.
|
|
257
|
+
* - `string[]` — route patterns (exact match or wildcard `*` suffix)
|
|
258
|
+
* - `(pathname: string) => boolean` — custom predicate
|
|
259
|
+
* - `undefined` — show on all pages (default)
|
|
260
|
+
*/
|
|
261
|
+
showOn?: string[] | ((pathname: string) => boolean);
|
|
262
|
+
/** Which feedback categories to show as pills. Default: all four. */
|
|
263
|
+
categories?: FeedbackCategory[];
|
|
210
264
|
};
|
|
211
265
|
}
|
|
212
266
|
interface ScreenshotIntent {
|
|
@@ -226,6 +280,8 @@ interface FeedbackPayload {
|
|
|
226
280
|
createdAt: string;
|
|
227
281
|
blocfeed_id: string;
|
|
228
282
|
message: string;
|
|
283
|
+
/** Feedback category selected by the user. */
|
|
284
|
+
category?: FeedbackCategory;
|
|
229
285
|
selection?: ElementDescriptor;
|
|
230
286
|
screenshots?: CaptureResult;
|
|
231
287
|
/** Lightweight screenshot metadata sent instead of base64 dataUrls. */
|
|
@@ -277,6 +333,7 @@ interface BlocFeedController {
|
|
|
277
333
|
setConfig: (nextConfig: BlocFeedConfig) => void;
|
|
278
334
|
submit: (message: string, options?: {
|
|
279
335
|
capture?: CaptureConfig;
|
|
336
|
+
category?: FeedbackCategory;
|
|
280
337
|
}) => Promise<SubmitResult>;
|
|
281
338
|
/** Internal: used by UI to access the live element handle. */
|
|
282
339
|
__unsafeGetSelectedElement: () => Element | null;
|
|
@@ -284,4 +341,4 @@ interface BlocFeedController {
|
|
|
284
341
|
}
|
|
285
342
|
declare function createBlocFeedController(config: BlocFeedConfig): BlocFeedController;
|
|
286
343
|
|
|
287
|
-
export { type BlocFeedConfig as B, type CaptureConfig as C, type DiagnosticsConfig as D, type ElementDescriptor as E, type
|
|
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 Rect as R, type SubmitResult as S, type ThemeConfig as T, 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 ScreenshotAdapter as n, type ScreenshotAdapterOptions as o, type ScreenshotIntent as p, type ScreenshotMime as q, type SecurityConfig as r, type SecurityFinding as s, type SecuritySnapshot as t, type SessionPhase as u, type TransportConfig as v, type TransportResult as w, type TriggerStyle as x, type StateListener as y, createBlocFeedController as z };
|