@hakam-aldeen-kh/blix 0.4.0 → 0.4.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/README.md +174 -11
  3. package/package.json +3 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,129 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.4.1] - 2026-08-24
9
+
10
+ ### Changed
11
+
12
+ - Documentation only — no code change.
13
+ - Added a Security section documenting what Blix captures, what reaches
14
+ IndexedDB and when, the four redacted header names, retention limits and
15
+ how to purge.
16
+ - Documented `preserve-log`: its default, the three toggles, and that
17
+ switching it on writes the session already in the buffer.
18
+ - Corrected the `dbName` section, which claimed the log persists across a
19
+ reload by default. It does not — persistence is opt-in.
20
+ - Noted that the package is ESM-only.
21
+ - Added `SECURITY.md`.
22
+
23
+ ## [0.4.0] - 2026-08-24
24
+
25
+ Identical in code to 0.3.3. It exists only to correct the version number: 0.3.3
26
+ was a feature release with a type-level break and should never have been a patch
27
+ bump. 0.3.3 is deprecated on npm in favour of this release. Everything below is
28
+ new relative to 0.3.2, not to 0.3.3.
29
+
30
+ ### Added
31
+
32
+ - Twelve panel themes, behind a new toolbar button. Dark: Midnight (default),
33
+ Carbon, Nord, Tokyo Night, One Dark, Mocha, Dracula, Gruvbox. Light: Daylight,
34
+ GitHub, Latte, Solar. Hovering or arrow-keying an entry applies it to the panel
35
+ behind the menu; moving away restores the previous one. The default preference
36
+ is `auto`, which follows the host app's light/dark class on `<html>`. The
37
+ choice is persisted with the panel's other preferences.
38
+ - A format switch on every payload pane — Tree, Table, JSON, YAML, Text. It
39
+ applies to request and response bodies, realtime frames, the encrypted
40
+ envelope, Redux actions and both Redux state views. Copy returns whichever
41
+ format is on screen (Table copies CSV, YAML copies YAML). The choice is
42
+ persisted.
43
+ - Folding in the JSON view, with a **Collapse all** action; Alt-click in the Tree
44
+ expands or collapses an entire subtree.
45
+ - Keyboard navigation in the payload tree (arrow keys to move, expand, collapse
46
+ and step in/out; Enter to toggle) and in menus (arrows, Home/End, Enter, and
47
+ Esc to close and return focus to the button that opened it). The `?` cheatsheet
48
+ documents both, plus the theme picker and the formats.
49
+ - An export menu with six formats — HAR, JSON, NDJSON, Markdown table (to the
50
+ clipboard), CSV and a cURL script — and a **Shown** / **All** scope toggle that
51
+ shows both counts. Export previously always took the unfiltered buffer,
52
+ whatever the list was showing; Shown is now the default. CSV is written with a
53
+ UTF-8 BOM so Excel reads non-ASCII correctly.
54
+ - A store slice picker on the Redux **State** tab. It opens on the slice the
55
+ selected action wrote to when the action touched exactly one, marks every slice
56
+ the action changed, and keeps `root` selectable.
57
+ - **Copy as fetch** in the row context menu, alongside Copy as cURL.
58
+ - Horizontal scrolling for the tab row and the slice picker when they overflow,
59
+ with edge fades, wheel support and arrow buttons.
60
+ - Accessible names and pressed/expanded state on the toolbar controls, and
61
+ tab/tablist roles on the tab and slice strips.
62
+
63
+ ### Changed
64
+
65
+ - The **Preview** and **Response** tabs are merged into a single **Response**
66
+ tab. Both previously rendered the same field — one as a tree, one as raw JSON —
67
+ so the rendering is now chosen with the format switch instead of by picking a
68
+ tab.
69
+ - `MonitorPrefs`, exported from `@hakam-aldeen-kh/blix/capture`, gained two
70
+ required fields: `theme: string` and `dataFormat: string`. This is a
71
+ compile-time break for anyone constructing a `MonitorPrefs` object literal.
72
+ `BlixProps` and every function signature in `/capture` are unchanged.
73
+ - HAR download filename: `network-<timestamp>.har` → `blix-<timestamp>.har`.
74
+ Every other export uses the same `blix-<timestamp>.<ext>` form.
75
+ - A press anywhere outside an open menu now dismisses it, including inside the
76
+ panel; previously only a press outside the panel root did, so a menu left open
77
+ over the panel could only be closed with Esc or its own button. The press is
78
+ not swallowed — it still selects the row or activates the button underneath.
79
+ - The status bar and the detail pane's empty states name what a row is in the
80
+ active section — requests, connections, actions or queries — instead of calling
81
+ every row a "request".
82
+ - Every colour the panel paints now comes from the active theme's tokens: status
83
+ colours, method and section accents, waterfall bars, timing segments and JSON
84
+ syntax highlighting. The previous split, with a light palette in CSS and a
85
+ duplicate dark one in JS, is gone.
86
+ - WebSocket, Redux and Query rows take their section's accent colour rather than
87
+ the shared fallback that HTTP verbs fell through to.
88
+ - The docked badge reads "Blix" rather than "Dev", and the toolbar shows a BLIX
89
+ wordmark beside the status dot.
90
+
91
+ ### Fixed
92
+
93
+ - Generated downloads revoke their object URL on the next task instead of
94
+ immediately after the click, which could race the browser's read of the blob
95
+ and save an empty file in Safari.
96
+
97
+ ## [0.3.3] - 2026-08-24 — DEPRECATED
98
+
99
+ Released in error as a patch bump: it was a feature release that also added two
100
+ required fields to the publicly exported `MonitorPrefs` type. Superseded by
101
+ 0.4.0, which is identical in code. Deprecated on npm — use 0.4.0. Its contents
102
+ are listed under 0.4.0.
103
+
104
+ ## [0.3.2] - 2026-08-19
105
+
106
+ ### Added
107
+
108
+ - `./package.json` as an export subpath, a top-level `types` field, `engines`
109
+ declaring Node >= 18, and package metadata (repository, homepage, bugs,
110
+ keywords).
111
+
112
+ ### Changed
113
+
114
+ - `peerDependencies` narrowed from `*` to `axios ^1.0.0`,
115
+ `@reduxjs/toolkit ^2.0.0` and `@tanstack/react-query ^5.0.0`. Narrowing a peer
116
+ range is breaking by convention and this shipped as a patch — though 0.3.2 was
117
+ the first version on the public npm registry, so no npm consumer could have
118
+ been affected.
119
+ - License: `UNLICENSED` → MIT.
120
+ - Published to the public npm registry instead of GitHub Packages.
121
+
122
+ ### Fixed
123
+
124
+ - `tapQueryClient`'s disposer now clears the client's internal tapped mark. A
125
+ dispose-then-reinstall cycle — exactly what React Strict Mode does to an effect
126
+ returning this disposer — hit the idempotency guard and became a permanent
127
+ no-op, leaving the Query tab silently empty for the rest of the session.
128
+
129
+ Versions before 0.3.2 were published to GitHub Packages and are not available on the public npm registry.
package/README.md CHANGED
@@ -32,6 +32,13 @@ while resolving that import. devDependencies have to be present at build time.
32
32
  Dropping them from the final runtime image is fine: nothing from Blix reaches
33
33
  the production output anyway.
34
34
 
35
+ The package ships **ESM only**. Both entry points declare `"types"` and
36
+ `"import"` in `exports`, with no `"require"` and no `"default"` fallback, so
37
+ `require("@hakam-aldeen-kh/blix")` and CJS-only tooling — an older Jest config
38
+ is the usual one — fail with `ERR_PACKAGE_PATH_NOT_EXPORTED`. That is by
39
+ design, not a packaging bug: a dev tool that is eliminated from production has
40
+ no reason to carry a second build output.
41
+
35
42
  ### Peer dependencies
36
43
 
37
44
  `react` and `react-dom` (v19) are required. `axios`, `@reduxjs/toolkit` and
@@ -351,12 +358,14 @@ as a bounded hex preview plus byte length). They go through the same
351
358
  serialization and truncation rules as the plaintext bodies, both in the panel
352
359
  and in IndexedDB, so a multi-megabyte ciphertext cannot blow out the log.
353
360
 
354
- > **Redaction.** Blix masks sensitive *headers* (`authorization`, `cookie`, …).
355
- > It does **not**, and cannot, redact anything inside the values you pass here
356
- > they are bodies, and Blix has no way to tell ciphertext from plaintext. If
357
- > you pass an already-decrypted body as `response`, whatever secrets it
358
- > contains are shown in the panel verbatim and written to IndexedDB when
359
- > preserve-log is on. Pass the wire form, not the decrypted one.
361
+ > **Redaction.** Blix masks sensitive *headers* and the list is closed at
362
+ > exactly four, matched exactly: `authorization`, `cookie`, `set-cookie`,
363
+ > `x-api-key`. It does **not**, and cannot, redact anything inside the values
364
+ > you pass here they are bodies, and Blix has no way to tell ciphertext from
365
+ > plaintext. If you pass an already-decrypted body as `response`, whatever
366
+ > secrets it contains are shown in the panel verbatim and written to IndexedDB
367
+ > when preserve-log is on. Pass the wire form, not the decrypted one. See
368
+ > [Security](#security) for the full picture.
360
369
 
361
370
  #### `withInitiatorCapture(instance)`
362
371
 
@@ -684,11 +693,15 @@ There is no API for adding your own; a theme is ~20 colours in
684
693
 
685
694
  ### `dbName` — when you need it
686
695
 
687
- The panel persists its log to IndexedDB so it survives a reload. IndexedDB is
688
- scoped **per origin**, not per app so two apps served from the same origin
689
- (different ports in dev are different origins, but path-based routing,
690
- multi-zone Next.js setups and anything behind one reverse proxy are not) both
691
- open `nm-devtools` and interleave their logs into one database.
696
+ The panel can persist its log to IndexedDB so it survives a reload, but only
697
+ when you opt in: **preserve-log is off by default**, and while it is off
698
+ nothing is written to disk. See [Security](#security) for what the toggle does
699
+ and what lands there.
700
+
701
+ IndexedDB is scoped **per origin**, not per app — so two apps served from the
702
+ same origin (different ports in dev are different origins, but path-based
703
+ routing, multi-zone Next.js setups and anything behind one reverse proxy are
704
+ not) both open `nm-devtools` and interleave their logs into one database.
692
705
 
693
706
  Give each app its own name to keep them separate:
694
707
 
@@ -788,6 +801,156 @@ example above, which is written in the guarded form.
788
801
 
789
802
  ---
790
803
 
804
+ ## Security
805
+
806
+ Blix is a debugger, and it captures what a debugger has to capture: **full
807
+ request and response bodies, request and response headers, Redux action
808
+ payloads and state diffs, and realtime frames**. It captures them in
809
+ plaintext — the request body *before* your encryption interceptor runs, the
810
+ response body *after* your decryption interceptor. That is the whole point of
811
+ it, and it means the log holds whatever your traffic holds, credentials
812
+ included.
813
+
814
+ By default all of that is **in memory only**. Nothing is written to disk, and
815
+ a reload starts clean.
816
+
817
+ All of it is dev-only regardless. Capture is gated on
818
+ `process.env.NODE_ENV === "development" && typeof window !== "undefined"`, and
819
+ the database is opened only when the panel mounts — see
820
+ [Production elimination](#production-elimination).
821
+
822
+ ### What reaches disk, and when
823
+
824
+ **Preserve-log** is the sole gate on disk writes. It is off by default, and
825
+ while it is off nothing Blix captures reaches IndexedDB — the store is
826
+ actively cleared on every panel mount.
827
+
828
+ Three ways to toggle it:
829
+
830
+ | Where | Note |
831
+ | --- | --- |
832
+ | Toolbar button | Hidden in the compact layout |
833
+ | **⋯ More actions** overflow menu | Always available |
834
+ | `Shift+L` | Always available |
835
+
836
+ **Turning it on is retroactive.** The toggle does not mean "from now on".
837
+ Switching it on writes every entry already sitting in the live buffer — the
838
+ session you have *already* captured — to disk immediately, along with
839
+ everything that follows. If you have just reproduced a login flow and then
840
+ reach for the toggle, you have written that login flow to disk. Read that
841
+ again before you assume otherwise; it is the one behaviour here that
842
+ reasonably surprises people.
843
+
844
+ With preserve-log on, this is what is kept:
845
+
846
+ | Captured | Persisted |
847
+ | --- | --- |
848
+ | HTTP entries — bodies, headers, timings | yes |
849
+ | Realtime frames | yes |
850
+ | The encrypted envelope, if you call `captureEncrypted` | yes |
851
+ | Panel preferences and budget totals | yes — preferences are also mirrored to `localStorage` |
852
+ | Redux actions, payloads and diffs | only if you pin the row |
853
+ | Query cache rows | only if you pin the row |
854
+
855
+ ### What is redacted
856
+
857
+ Exactly four header names, and nothing else:
858
+
859
+ | Header | Match |
860
+ | --- | --- |
861
+ | `authorization` | exact, case-insensitive |
862
+ | `cookie` | exact, case-insensitive |
863
+ | `set-cookie` | exact, case-insensitive |
864
+ | `x-api-key` | exact, case-insensitive |
865
+
866
+ The match is **exact on the full header name** — not a prefix, not a
867
+ substring, not a pattern. Near-miss names are *not* covered and are written in
868
+ the clear: `x-auth-token` and `api-key` are the two that most often catch
869
+ people out, and `proxy-authorization`, `x-csrf-token` and
870
+ `x-amz-security-token` are equally uncovered. If your auth travels in a header
871
+ that is not one of the four above, it is captured verbatim.
872
+
873
+ Masking is partial rather than total: for a value longer than 12 characters
874
+ the first 8 and last 4 survive, so you can still tell which token you sent.
875
+ Shorter values are replaced outright.
876
+
877
+ **Nothing inside a body is redacted.** Request bodies, response bodies, error
878
+ payloads, the encrypted request/response values, Redux payloads and diffs, and
879
+ realtime frames all pass through a size-only walker — it truncates large
880
+ values and never once inspects a key name. A `password`, `ssn` or
881
+ `refreshToken` field is captured, and with preserve-log on written to disk,
882
+ exactly as it appears. Blix has no mechanism to do otherwise: it has no schema
883
+ for your payloads and no way to tell a secret from any other string.
884
+
885
+ ### Retention and clearing
886
+
887
+ | Bound | Value |
888
+ | --- | --- |
889
+ | Records | 200 |
890
+ | Total size | 24 MB |
891
+ | Per payload field | 512 KB |
892
+ | Eviction | oldest first, once either bound is exceeded |
893
+ | Time-based expiry | none |
894
+
895
+ There is **no TTL of any kind**. A record leaves the database when 200 newer
896
+ records or 24 MB of newer traffic push it out, or when you clear it yourself.
897
+ On a low-traffic app with preserve-log left on, a captured token stays in the
898
+ browser profile indefinitely.
899
+
900
+ > **Realtime frames are an exception to the size bound.** They are stored
901
+ > without truncation and are under-counted against the byte budget — a record
902
+ > is charged a flat allowance regardless of how many frames it carries, and a
903
+ > connection can hold hundreds. A long-lived realtime session can therefore
904
+ > occupy considerably more on disk than the 24 MB figure implies, and eviction
905
+ > will not reclaim it. The record and size caps hold for HTTP entries.
906
+ > Tracked in [#N](https://github.com/Hakam-aldeen-Kh/blix/issues/N).
907
+
908
+ To purge, use the persisted-size label in the status bar — the one reading
909
+ `12 saved · 3.4 MB`. It is the control: click once to arm it, at which point
910
+ it changes to `Purge saved log?`, and click again to delete the database.
911
+
912
+ Switching preserve-log **off** also clears the stored entries, so turning it
913
+ off is itself a way to drop everything Blix has written. What survives is the
914
+ database and your panel preferences, not the captured bodies.
915
+
916
+ **The purge control is only rendered while preserve-log is on**, so once you
917
+ have switched it off there is nothing left in the UI to press — and there is
918
+ no programmatic API for it either. Use Purge when you want the database gone
919
+ outright; switch off when clearing the entries is enough.
920
+
921
+ ### Threat model
922
+
923
+ IndexedDB is scoped **per origin, not per app**, and it is not encrypted at
924
+ rest. Any script running on that origin can read Blix's database — including
925
+ browser extension content scripts with access to the origin. Whatever you
926
+ capture is readable by whatever you have installed.
927
+
928
+ Export and copy move captured data out of the browser entirely:
929
+
930
+ | Path | Carries |
931
+ | --- | --- |
932
+ | HAR export | Decrypted request and response bodies |
933
+ | JSON / NDJSON export | Everything captured — frames, Redux diffs, timings |
934
+ | **Copy as cURL** / **Copy as fetch** | Headers and bodies, with `Authorization` masked — so the output is not a working request |
935
+
936
+ HAR is the one to watch. It is a plain JSON file carrying your decrypted
937
+ bodies, and it is the artifact most likely to end up attached to a ticket.
938
+
939
+ ### If you handle sensitive data
940
+
941
+ - **Leave preserve-log off unless you actively need it.** It is off by
942
+ default. In-memory capture already gives you the entire panel; the toggle
943
+ buys you nothing but survival across a reload.
944
+ - **Purge after any session that captured an auth flow** — and purge *before*
945
+ you switch preserve-log back off, or the control disappears on you.
946
+ - **Use the Redux `ignore` option** for action types that carry credentials or
947
+ personal data, so they are never captured in the first place. See
948
+ [Redux](#redux--createreduxmonitormiddlewareoptions).
949
+ - **Treat an exported HAR as a credential-bearing file.** Do not attach one to
950
+ a public issue, and do not commit one.
951
+
952
+ ---
953
+
791
954
  ## Entry points
792
955
 
793
956
  | Import | Contents | `"use client"` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hakam-aldeen-kh/blix",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Dev-tools panel for React apps — HTTP, Redux, Query and Realtime monitoring.",
5
5
  "keywords": [
6
6
  "devtools",
@@ -63,7 +63,8 @@
63
63
  "./package.json": "./package.json"
64
64
  },
65
65
  "files": [
66
- "dist"
66
+ "dist",
67
+ "CHANGELOG.md"
67
68
  ],
68
69
  "engines": {
69
70
  "node": ">=18"