@hakam-aldeen-kh/blix 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -7
- package/dist/{DevToolsPanel-WD327OP5.js → DevToolsPanel-5XOY5WN5.js} +28 -4
- package/dist/capture/index.js +2 -2
- package/dist/{chunk-2V2YJ5ML.js → chunk-IWI5YFYP.js} +14 -5
- package/dist/{chunk-GGVNJ6QZ.js → chunk-TV2KC74D.js} +1 -1
- package/dist/index.js +14 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,8 @@ a dockable panel with cross-source links, diffing, replay and HAR/cURL
|
|
|
7
7
|
export.
|
|
8
8
|
|
|
9
9
|
The entire panel is eliminated from production builds — see
|
|
10
|
-
[Production elimination](#production-elimination).
|
|
10
|
+
[Production elimination](#production-elimination). To keep it on a staging
|
|
11
|
+
server, see [Enabling Blix in a production build](#enabling-blix-in-a-production-build-staging).
|
|
11
12
|
|
|
12
13
|
Release notes are in [CHANGELOG.md](https://github.com/Hakam-aldeen-Kh/blix/blob/main/CHANGELOG.md).
|
|
13
14
|
|
|
@@ -397,7 +398,9 @@ never creating one of its own — and the panel updates.
|
|
|
397
398
|
It is a **silent no-op** — never a throw, never a console warning — in every
|
|
398
399
|
one of these:
|
|
399
400
|
|
|
400
|
-
- outside development (`process.env.NODE_ENV !== "development"`)
|
|
401
|
+
- outside development (`process.env.NODE_ENV !== "development"`), unless the
|
|
402
|
+
build opted in with `NEXT_PUBLIC_BLIX=true` (see
|
|
403
|
+
[staging](#enabling-blix-in-a-production-build-staging));
|
|
401
404
|
- **outside the browser** — the gate is also `typeof window !== "undefined"`,
|
|
402
405
|
so every call made while rendering on the server does nothing, by design (see
|
|
403
406
|
[Production elimination](#production-elimination));
|
|
@@ -721,7 +724,11 @@ import { store } from "@/src/store";
|
|
|
721
724
|
// module graph, which fails the production build. Importing them here keeps
|
|
722
725
|
// that evaluation on the client side of the boundary.
|
|
723
726
|
export default function BlixMount() {
|
|
724
|
-
if (
|
|
727
|
+
if (
|
|
728
|
+
process.env.NODE_ENV !== "development" &&
|
|
729
|
+
process.env.NEXT_PUBLIC_BLIX !== "true"
|
|
730
|
+
)
|
|
731
|
+
return null;
|
|
725
732
|
return <Blix store={store} apiClient={apiClient} dbName="my-app" />;
|
|
726
733
|
}
|
|
727
734
|
```
|
|
@@ -798,6 +805,14 @@ field carries its own count — `8 of 56` — and a clear button, so you never
|
|
|
798
805
|
have to read the status bar to know one is applied. `.*` widens the search to
|
|
799
806
|
request and response bodies.
|
|
800
807
|
|
|
808
|
+
**The red bin beside Export clears the source you are reading**, and only
|
|
809
|
+
that one: emptying Redux leaves Network alone. Pinned entries survive it, and
|
|
810
|
+
it is disabled when the source has nothing left to clear. To empty every
|
|
811
|
+
source at once, use **Clear log** in the overflow menu, or `⇧C`.
|
|
812
|
+
|
|
813
|
+
The panel opens **floating** the first time. Dock it to the bottom or the
|
|
814
|
+
right from the header; the choice is saved per project.
|
|
815
|
+
|
|
801
816
|
**`Ctrl/⌘ K` opens the command palette**, and for several things it is the only
|
|
802
817
|
way in — sort order, row density, dock position, the copy formats, the filter
|
|
803
818
|
syntax. The header spends its width on what you read constantly; everything you
|
|
@@ -1014,8 +1029,55 @@ payloads for the lifetime of the server. Keeping the server-side singleton
|
|
|
1014
1029
|
permanently empty is the point.
|
|
1015
1030
|
|
|
1016
1031
|
Practically: a `captureEncrypted` call that runs during SSR does nothing, and
|
|
1017
|
-
`<Blix />`
|
|
1018
|
-
|
|
1032
|
+
`<Blix />` renders nothing there. It also renders nothing during hydration, so
|
|
1033
|
+
its first client render matches the server's HTML; the panel mounts on the
|
|
1034
|
+
render right after. (Before 0.9.0 it branched on `typeof window` instead,
|
|
1035
|
+
which Next.js reported as a hydration mismatch on every load.)
|
|
1036
|
+
|
|
1037
|
+
### Enabling Blix in a production build (staging)
|
|
1038
|
+
|
|
1039
|
+
A staging or test server usually runs a production build, where the panel is
|
|
1040
|
+
gone by design. Setting `NODE_ENV=development` for that build does not bring
|
|
1041
|
+
it back on Next.js 16: `next build` runs on Turbopack and inlines `NODE_ENV`
|
|
1042
|
+
as `"production"` whatever the environment says.
|
|
1043
|
+
|
|
1044
|
+
Opt in explicitly instead, in that server's env file:
|
|
1045
|
+
|
|
1046
|
+
```bash
|
|
1047
|
+
NEXT_PUBLIC_BLIX=true
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
The panel and the capture layer both check it next to `NODE_ENV`, as the same
|
|
1051
|
+
kind of literal comparison, so Next.js inlines it at build time.
|
|
1052
|
+
|
|
1053
|
+
> **Set `NEXT_PUBLIC_BLIX=false` in your production env file.** Next.js only
|
|
1054
|
+
> inlines variables that are defined. Left unset, the comparison stays in the
|
|
1055
|
+
> bundle as a runtime check: the panel still never loads — the variable is
|
|
1056
|
+
> undefined in the browser — but its chunk is emitted into your production
|
|
1057
|
+
> build. `false` lets the bundler fold the check and drop the chunk.
|
|
1058
|
+
|
|
1059
|
+
**Never set `NEXT_PUBLIC_BLIX=true` for a build that real users get.** It
|
|
1060
|
+
ships the monitor to them, and with it everything it captures — see
|
|
1061
|
+
[Security](#security).
|
|
1062
|
+
|
|
1063
|
+
**Your own guards need the variable too.** A mount component that returns
|
|
1064
|
+
`null` outside development (as in [Mounting the panel](#mounting-the-panel)),
|
|
1065
|
+
or a call-site guard around `attachHttpMonitor` (below), keeps Blix off on
|
|
1066
|
+
staging regardless of the variable. Add it to each:
|
|
1067
|
+
|
|
1068
|
+
```ts
|
|
1069
|
+
if (
|
|
1070
|
+
(process.env.NODE_ENV === "development" ||
|
|
1071
|
+
process.env.NEXT_PUBLIC_BLIX === "true") &&
|
|
1072
|
+
typeof window !== "undefined"
|
|
1073
|
+
) {
|
|
1074
|
+
attachHttpMonitor(apiClient);
|
|
1075
|
+
}
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
The variable is read under its `NEXT_PUBLIC_` name, which is a Next.js
|
|
1079
|
+
convention. The opt-in has been verified with Next.js 16 (Turbopack) only;
|
|
1080
|
+
other bundlers do not inline that name by default.
|
|
1019
1081
|
|
|
1020
1082
|
### Guard your call sites
|
|
1021
1083
|
|
|
@@ -1085,10 +1147,12 @@ option to keep a third party's traffic out of the log.
|
|
|
1085
1147
|
By default all of that is **in memory only**. Nothing is written to disk, and
|
|
1086
1148
|
a reload starts clean.
|
|
1087
1149
|
|
|
1088
|
-
All of it is dev-only
|
|
1150
|
+
All of it is dev-only unless you opt a build in. Capture is gated on
|
|
1089
1151
|
`process.env.NODE_ENV === "development" && typeof window !== "undefined"`, and
|
|
1090
1152
|
the database is opened only when the panel mounts — see
|
|
1091
|
-
[Production elimination](#production-elimination).
|
|
1153
|
+
[Production elimination](#production-elimination). A build made with
|
|
1154
|
+
`NEXT_PUBLIC_BLIX=true` captures exactly as development does, so everything in
|
|
1155
|
+
this section applies to whoever uses that build.
|
|
1092
1156
|
|
|
1093
1157
|
### What reaches disk, and when
|
|
1094
1158
|
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
setReduxIgnoreList,
|
|
31
31
|
summarizeInitiator,
|
|
32
32
|
truncateForPersist
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-IWI5YFYP.js";
|
|
34
34
|
|
|
35
35
|
// src/capture/monitorDatabases.ts
|
|
36
36
|
var REGISTRY_KEY = `${DB_PREFIX}databases`;
|
|
@@ -469,7 +469,7 @@ var LEGACY_KEYS = [
|
|
|
469
469
|
];
|
|
470
470
|
var DEFAULT_PREFS = {
|
|
471
471
|
rev: 0,
|
|
472
|
-
mode: "
|
|
472
|
+
mode: "float",
|
|
473
473
|
bottomH: 420,
|
|
474
474
|
rightW: 520,
|
|
475
475
|
float: { pos: null, size: { w: 920, h: 600 }, maximized: false },
|
|
@@ -4699,6 +4699,9 @@ function MonitorToolbar(props) {
|
|
|
4699
4699
|
onExportMenu,
|
|
4700
4700
|
exportOpen,
|
|
4701
4701
|
exportDisabled,
|
|
4702
|
+
onClearSection,
|
|
4703
|
+
clearDisabled,
|
|
4704
|
+
sectionLabel,
|
|
4702
4705
|
onThemeMenu,
|
|
4703
4706
|
themeOpen,
|
|
4704
4707
|
themeLabel,
|
|
@@ -4825,6 +4828,18 @@ function MonitorToolbar(props) {
|
|
|
4825
4828
|
children: /* @__PURE__ */ jsx16(Icon, { name: "download", size: 13 })
|
|
4826
4829
|
}
|
|
4827
4830
|
),
|
|
4831
|
+
/* @__PURE__ */ jsx16(
|
|
4832
|
+
"button",
|
|
4833
|
+
{
|
|
4834
|
+
type: "button",
|
|
4835
|
+
className: "nm-ibtn nm-ibtn-danger",
|
|
4836
|
+
onClick: onClearSection,
|
|
4837
|
+
disabled: clearDisabled,
|
|
4838
|
+
"aria-label": `Clear the ${sectionLabel} log`,
|
|
4839
|
+
title: `Clear the ${sectionLabel} log \u2014 other sources and pinned entries are kept`,
|
|
4840
|
+
children: /* @__PURE__ */ jsx16(Icon, { name: "clear", size: 13 })
|
|
4841
|
+
}
|
|
4842
|
+
),
|
|
4828
4843
|
/* @__PURE__ */ jsx16(
|
|
4829
4844
|
"button",
|
|
4830
4845
|
{
|
|
@@ -8583,6 +8598,9 @@ var HEADER = `
|
|
|
8583
8598
|
.nm-ibtn:hover { background: var(--bx-hover); }
|
|
8584
8599
|
.nm-ibtn.active, .nm-ibtn[aria-expanded="true"] { background: var(--bx-sel); color: var(--bx-fg); }
|
|
8585
8600
|
.nm-ibtn:disabled { color: var(--bx-mark); cursor: default; background: transparent; }
|
|
8601
|
+
/* Destructive: the header's per-source clear. Red while it can do something;
|
|
8602
|
+
the disabled rule above still greys it out when there is nothing to clear. */
|
|
8603
|
+
.nm-ibtn-danger:not(:disabled) { color: var(--bx-err); }
|
|
8586
8604
|
.nm-hsep { width: 1px; height: 16px; flex: none; background: var(--bx-line); }
|
|
8587
8605
|
`;
|
|
8588
8606
|
var RAIL = `
|
|
@@ -10524,7 +10542,7 @@ var EMPTY_COPY = {
|
|
|
10524
10542
|
}
|
|
10525
10543
|
};
|
|
10526
10544
|
function DevTools() {
|
|
10527
|
-
var _a, _b, _c, _d, _e;
|
|
10545
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
10528
10546
|
const { apiClient } = useContext4(BlixContext);
|
|
10529
10547
|
const entries = useSyncExternalStore2(
|
|
10530
10548
|
networkMonitor.subscribe,
|
|
@@ -11214,6 +11232,12 @@ function DevTools() {
|
|
|
11214
11232
|
onExportMenu: setExportAnchor,
|
|
11215
11233
|
exportOpen: exportAnchor !== null,
|
|
11216
11234
|
exportDisabled: entries.length === 0,
|
|
11235
|
+
onClearSection: () => {
|
|
11236
|
+
networkMonitor.clear((e) => sectionOf(e) === section);
|
|
11237
|
+
selection.clear();
|
|
11238
|
+
},
|
|
11239
|
+
clearDisabled: !sectionEntries.some((e) => !e.pinned),
|
|
11240
|
+
sectionLabel: (_f = (_e = SECTION_DEFS.find((s) => s.id === section)) == null ? void 0 : _e.label) != null ? _f : section,
|
|
11217
11241
|
onThemeMenu: setThemeAnchor,
|
|
11218
11242
|
themeOpen: themeAnchor !== null,
|
|
11219
11243
|
themeLabel: theme.label,
|
|
@@ -11369,7 +11393,7 @@ function DevTools() {
|
|
|
11369
11393
|
noun: nouns.many,
|
|
11370
11394
|
totalBytes: list.totalBytes,
|
|
11371
11395
|
slowestMs,
|
|
11372
|
-
failingHint: (
|
|
11396
|
+
failingHint: (_g = firstFailing == null ? void 0 : firstFailing.url) != null ? _g : null,
|
|
11373
11397
|
pinnedCount,
|
|
11374
11398
|
persistedCount: persisted.count,
|
|
11375
11399
|
persistedLabel: prefs2.preserveLog && hydration2 === "ready" ? `${persisted.count} saved \xB7 ${formatBytes(persisted.bytes)}` : null,
|
package/dist/capture/index.js
CHANGED
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
attachHttpMonitor,
|
|
4
4
|
captureEncrypted,
|
|
5
5
|
tapRealtimeAdapter
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-TV2KC74D.js";
|
|
7
7
|
import {
|
|
8
8
|
createReduxMonitorMiddleware,
|
|
9
9
|
tapQueryClient,
|
|
10
10
|
withInitiatorCapture
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-IWI5YFYP.js";
|
|
12
12
|
export {
|
|
13
13
|
attachFetchMonitor,
|
|
14
14
|
attachHttpMonitor,
|
|
@@ -19,7 +19,7 @@ var __spreadValues = (a, b) => {
|
|
|
19
19
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
20
|
|
|
21
21
|
// src/capture/monitorConfig.ts
|
|
22
|
-
var MONITOR_ENABLED = process.env.NODE_ENV === "development" && typeof window !== "undefined";
|
|
22
|
+
var MONITOR_ENABLED = (process.env.NODE_ENV === "development" || process.env.NEXT_PUBLIC_BLIX === "true") && typeof window !== "undefined";
|
|
23
23
|
var NM_SCHEMA_VERSION = 1;
|
|
24
24
|
var DB_PREFIX = "blix:";
|
|
25
25
|
var LEGACY_DB_NAME = "nm-devtools";
|
|
@@ -386,11 +386,20 @@ var NetworkMonitor = class {
|
|
|
386
386
|
this.entries = next;
|
|
387
387
|
(_b = this.sink) == null ? void 0 : _b.onDirty(parentId);
|
|
388
388
|
}
|
|
389
|
-
/**
|
|
390
|
-
|
|
389
|
+
/**
|
|
390
|
+
* Clears the log, keeping pinned entries — that is what pinning is for.
|
|
391
|
+
*
|
|
392
|
+
* With `match`, only the unpinned entries it accepts go — the header's
|
|
393
|
+
* per-source clear passes one, so emptying Redux leaves Network alone. The
|
|
394
|
+
* predicate comes from the caller because "which source is this entry" is
|
|
395
|
+
* a panel concept (`sectionOf`), not a capture one.
|
|
396
|
+
*/
|
|
397
|
+
clear(match) {
|
|
391
398
|
var _a, _b;
|
|
392
|
-
const
|
|
393
|
-
const
|
|
399
|
+
const goes = (e) => !e.pinned && (!match || match(e));
|
|
400
|
+
const survivors = this.entries.filter((e) => !goes(e));
|
|
401
|
+
const removed = this.entries.filter(goes).map((e) => e.id);
|
|
402
|
+
if (removed.length === 0) return;
|
|
394
403
|
this.entries = survivors;
|
|
395
404
|
this.reindex();
|
|
396
405
|
if (survivors.length === 0) {
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
attachHttpMonitor,
|
|
5
5
|
captureEncrypted,
|
|
6
6
|
tapRealtimeAdapter
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-TV2KC74D.js";
|
|
8
8
|
import {
|
|
9
9
|
BlixContext
|
|
10
10
|
} from "./chunk-PQIOIMAQ.js";
|
|
@@ -13,23 +13,31 @@ import {
|
|
|
13
13
|
createReduxMonitorMiddleware,
|
|
14
14
|
tapQueryClient,
|
|
15
15
|
withInitiatorCapture
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-IWI5YFYP.js";
|
|
17
17
|
|
|
18
18
|
// src/ui/Blix.tsx
|
|
19
|
-
import { lazy, Suspense, useEffect, useRef } from "react";
|
|
19
|
+
import { lazy, Suspense, useEffect, useRef, useSyncExternalStore } from "react";
|
|
20
20
|
import { jsx } from "react/jsx-runtime";
|
|
21
|
+
var noopSubscribe = () => () => {
|
|
22
|
+
};
|
|
21
23
|
function Blix({ store, apiClient, dbName }) {
|
|
22
24
|
const panelRef = useRef(null);
|
|
25
|
+
const mounted = useSyncExternalStore(
|
|
26
|
+
noopSubscribe,
|
|
27
|
+
() => true,
|
|
28
|
+
() => false
|
|
29
|
+
);
|
|
23
30
|
useEffect(() => {
|
|
24
|
-
if (process.env.NODE_ENV !== "development")
|
|
31
|
+
if (process.env.NODE_ENV !== "development" && process.env.NEXT_PUBLIC_BLIX !== "true")
|
|
32
|
+
return;
|
|
25
33
|
if (dbName) return;
|
|
26
34
|
console.warn(
|
|
27
35
|
'[blix] No dbName prop \u2014 using the shared database "blix:default".\nIndexedDB is scoped to the origin, not to your app, so every app served from this origin writes to that one database and entries from other projects will appear in this panel.\nFix: <Blix dbName="my-app" />\nExisting databases on this origin are listed under \u201CDatabases on this origin\u201D in the command palette (Ctrl/\u2318 K).'
|
|
28
36
|
);
|
|
29
37
|
}, []);
|
|
30
|
-
if (
|
|
38
|
+
if (mounted && (process.env.NODE_ENV === "development" || process.env.NEXT_PUBLIC_BLIX === "true")) {
|
|
31
39
|
if (!panelRef.current) {
|
|
32
|
-
panelRef.current = lazy(() => import("./DevToolsPanel-
|
|
40
|
+
panelRef.current = lazy(() => import("./DevToolsPanel-5XOY5WN5.js"));
|
|
33
41
|
}
|
|
34
42
|
const Panel = panelRef.current;
|
|
35
43
|
if (dbName) configureDbName(dbName);
|