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 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
- TransportConfig,
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&&lt(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&&lt(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 FeedbackApiResponse 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 BlocFeedState as a, type BlocFeedController as b, type BlocFeedError as c, type BlocFeedStrings as d, type BlocFeedUser as e, type CaptureDiagnostics as f, type CaptureResult as g, type ConsoleEntry as h, type FeedbackPayload as i, type MetadataConfig as j, type MetadataContext as k, type ScreenshotAdapter as l, type ScreenshotAdapterOptions as m, type ScreenshotIntent as n, type ScreenshotMime as o, type SessionPhase as p, type TransportConfig as q, type TransportResult as r, type TriggerStyle as s, type StateListener as t, createBlocFeedController as u };
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 };