@hansenexus/hud 0.1.0 → 0.2.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 +42 -23
- package/dist/index.d.ts +39 -0
- package/dist/index.js +399 -0
- package/package.json +45 -136
- package/chunk-0cj8dkk7.js +0 -127
- package/chunk-1atxwdhw.js +0 -48
- package/chunk-24vrg16w.js +0 -39
- package/chunk-3j9sm249.js +0 -26
- package/chunk-50pq4sg6.js +0 -41
- package/chunk-5gtx3pza.js +0 -9
- package/chunk-6gw4x08j.js +0 -264
- package/chunk-6jt8yg4m.js +0 -117
- package/chunk-71se605m.js +0 -85
- package/chunk-82nm9wcg.js +0 -316
- package/chunk-8tam7r3h.js +0 -68
- package/chunk-babjpqkb.js +0 -52
- package/chunk-c5y856ad.js +0 -98
- package/chunk-ghxh8926.js +0 -172
- package/chunk-hc2g0n35.js +0 -24
- package/chunk-k0vr8dsy.js +0 -179
- package/chunk-k50yfp5d.js +0 -166
- package/chunk-kyeb6qeq.js +0 -95
- package/chunk-msqkkqw7.js +0 -140
- package/chunk-nm82wr8r.js +0 -58
- package/chunk-r06bpmy6.js +0 -79
- package/chunk-t9eh4t0b.js +0 -89
- package/chunk-tf774c4w.js +0 -115
- package/chunk-v086j53b.js +0 -19
- package/chunk-wspz08q7.js +0 -67
- package/chunk-xezxydm3.js +0 -47
- package/chunk-xjn0896g.js +0 -574
- package/chunk-xztw0xyt.js +0 -113
- package/chunk-zzrpd92r.js +0 -172
- package/core/access.d.mts +0 -53
- package/core/access.js +0 -27
- package/core/api-types.d.mts +0 -146
- package/core/api-types.js +0 -9
- package/core/capabilities.d.mts +0 -27
- package/core/capabilities.js +0 -14
- package/core/capture-buffer.d.mts +0 -37
- package/core/capture-buffer.js +0 -9
- package/core/force-state.d.mts +0 -68
- package/core/force-state.js +0 -21
- package/core/mock-cookie.d.mts +0 -30
- package/core/mock-cookie.js +0 -15
- package/core/types.d.mts +0 -59
- package/core/types.js +0 -9
- package/hud-provider.d.mts +0 -38
- package/hud-provider.js +0 -15
- package/hud.d.mts +0 -15
- package/hud.js +0 -18
- package/index.d.mts +0 -94
- package/index.js +0 -170
- package/lazy-panel.d.mts +0 -28
- package/lazy-panel.js +0 -11
- package/panels/cicd-panel.d.mts +0 -9
- package/panels/cicd-panel.js +0 -13
- package/panels/debug-info-panel.d.mts +0 -21
- package/panels/debug-info-panel.js +0 -9
- package/panels/dispatch-panel.d.mts +0 -10
- package/panels/dispatch-panel.js +0 -14
- package/panels/env-info-panel.d.mts +0 -9
- package/panels/env-info-panel.js +0 -12
- package/panels/feature-flags-panel.d.mts +0 -26
- package/panels/feature-flags-panel.js +0 -9
- package/panels/force-state-panel.d.mts +0 -11
- package/panels/force-state-panel.js +0 -113
- package/panels/info-row.d.mts +0 -21
- package/panels/info-row.js +0 -14
- package/panels/mock-seed-panel.d.mts +0 -15
- package/panels/mock-seed-panel.js +0 -14
- package/panels/network-console-panel.d.mts +0 -6
- package/panels/network-console-panel.js +0 -9
- package/panels/role-switcher-panel.d.mts +0 -28
- package/panels/role-switcher-panel.js +0 -9
- package/panels/sessions-panel.d.mts +0 -6
- package/panels/sessions-panel.js +0 -13
- package/panels/tokens-panel.d.mts +0 -38
- package/panels/tokens-panel.js +0 -9
- package/panels/unlock-panel.d.mts +0 -33
- package/panels/unlock-panel.js +0 -14
- package/panels/variants-panel.d.mts +0 -51
- package/panels/variants-panel.js +0 -12
- package/panels/vitals-panel.d.mts +0 -11
- package/panels/vitals-panel.js +0 -15
- package/server/guard.d.mts +0 -42
- package/server/guard.js +0 -12
- package/server/unlock.d.mts +0 -28
- package/server/unlock.js +0 -108
- package/state-boundary.d.mts +0 -25
- package/state-boundary.js +0 -54
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
InfoRow,
|
|
3
|
-
PanelSection
|
|
4
|
-
} from "../chunk-kyeb6qeq.js";
|
|
5
|
-
import {
|
|
6
|
-
Badge,
|
|
7
|
-
Button
|
|
8
|
-
} from "../chunk-xztw0xyt.js";
|
|
9
|
-
import {
|
|
10
|
-
FORCED_STATES,
|
|
11
|
-
FORCE_STATE_PARAM,
|
|
12
|
-
getForceStateStore,
|
|
13
|
-
resolveForcedState
|
|
14
|
-
} from "../chunk-c5y856ad.js";
|
|
15
|
-
import"../chunk-5gtx3pza.js";
|
|
16
|
-
|
|
17
|
-
// panels/force-state-panel.tsx
|
|
18
|
-
import { Layers } from "lucide-react";
|
|
19
|
-
import { useSyncExternalStore } from "react";
|
|
20
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
-
"use client";
|
|
22
|
-
var CHOICES = [null, ...FORCED_STATES];
|
|
23
|
-
function subscribe(listener) {
|
|
24
|
-
return getForceStateStore().subscribe(listener);
|
|
25
|
-
}
|
|
26
|
-
function getSnapshot() {
|
|
27
|
-
return getForceStateStore().getSnapshot();
|
|
28
|
-
}
|
|
29
|
-
function ForceStatePanel() {
|
|
30
|
-
const store = getForceStateStore();
|
|
31
|
-
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
32
|
-
const hasOverrides = Object.keys(snapshot.overrides).length > 0;
|
|
33
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
34
|
-
className: "space-y-4 p-3",
|
|
35
|
-
"data-hud-force-state": "",
|
|
36
|
-
children: [
|
|
37
|
-
/* @__PURE__ */ jsxs(PanelSection, {
|
|
38
|
-
icon: Layers,
|
|
39
|
-
title: "State boundaries",
|
|
40
|
-
children: [
|
|
41
|
-
snapshot.boundaries.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
42
|
-
className: "px-2 text-[10px] text-muted-foreground",
|
|
43
|
-
children: "No state boundary on this page. Wrap a query branch in StateBoundary to list it here."
|
|
44
|
-
}) : snapshot.boundaries.map((boundary) => {
|
|
45
|
-
const override = snapshot.overrides[boundary.id] ?? null;
|
|
46
|
-
const effective = resolveForcedState(snapshot, boundary);
|
|
47
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
48
|
-
className: "space-y-1 px-2 py-1",
|
|
49
|
-
children: [
|
|
50
|
-
/* @__PURE__ */ jsxs("div", {
|
|
51
|
-
className: "flex items-center justify-between gap-2",
|
|
52
|
-
children: [
|
|
53
|
-
/* @__PURE__ */ jsxs("span", {
|
|
54
|
-
className: "truncate font-mono text-[10px]",
|
|
55
|
-
children: [
|
|
56
|
-
boundary.parentId ? "↳ " : "",
|
|
57
|
-
boundary.id
|
|
58
|
-
]
|
|
59
|
-
}),
|
|
60
|
-
/* @__PURE__ */ jsx(Badge, {
|
|
61
|
-
variant: effective ? "default" : "outline",
|
|
62
|
-
className: "text-[9px]",
|
|
63
|
-
children: effective ?? "real"
|
|
64
|
-
})
|
|
65
|
-
]
|
|
66
|
-
}),
|
|
67
|
-
/* @__PURE__ */ jsx("div", {
|
|
68
|
-
className: "flex gap-1",
|
|
69
|
-
children: CHOICES.map((choice) => /* @__PURE__ */ jsx(Button, {
|
|
70
|
-
size: "xs",
|
|
71
|
-
variant: override === choice ? "default" : "outline",
|
|
72
|
-
className: "h-6 flex-1 px-1 text-[10px]",
|
|
73
|
-
onClick: () => store.force(boundary.id, choice),
|
|
74
|
-
children: choice ?? "auto"
|
|
75
|
-
}, choice ?? "auto"))
|
|
76
|
-
})
|
|
77
|
-
]
|
|
78
|
-
}, boundary.id);
|
|
79
|
-
}),
|
|
80
|
-
hasOverrides && /* @__PURE__ */ jsx(Button, {
|
|
81
|
-
size: "xs",
|
|
82
|
-
variant: "ghost",
|
|
83
|
-
className: "h-6 w-full text-[10px]",
|
|
84
|
-
onClick: () => store.clearOverrides(),
|
|
85
|
-
children: "Reset all to auto"
|
|
86
|
-
})
|
|
87
|
-
]
|
|
88
|
-
}),
|
|
89
|
-
/* @__PURE__ */ jsxs(PanelSection, {
|
|
90
|
-
title: "URL param",
|
|
91
|
-
children: [
|
|
92
|
-
/* @__PURE__ */ jsx(InfoRow, {
|
|
93
|
-
label: `?${FORCE_STATE_PARAM}=`,
|
|
94
|
-
value: snapshot.param ? `${snapshot.param.target ? `${snapshot.param.target}:` : ""}${snapshot.param.state}` : "not set"
|
|
95
|
-
}),
|
|
96
|
-
/* @__PURE__ */ jsxs("p", {
|
|
97
|
-
className: "px-2 text-[10px] text-muted-foreground",
|
|
98
|
-
children: [
|
|
99
|
-
"?",
|
|
100
|
-
FORCE_STATE_PARAM,
|
|
101
|
-
"=loading|empty|error forces the top-level boundaries; ?",
|
|
102
|
-
FORCE_STATE_PARAM,
|
|
103
|
-
"=<id>:error targets one. A panel toggle wins over the param."
|
|
104
|
-
]
|
|
105
|
-
})
|
|
106
|
-
]
|
|
107
|
-
})
|
|
108
|
-
]
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
export {
|
|
112
|
-
ForceStatePanel
|
|
113
|
-
};
|
package/panels/info-row.d.mts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
3
|
-
import { LucideIcon } from 'lucide-react';
|
|
4
|
-
|
|
5
|
-
declare function PanelSection({ icon: Icon, title, children, }: {
|
|
6
|
-
icon?: LucideIcon;
|
|
7
|
-
title: string;
|
|
8
|
-
children: ReactNode;
|
|
9
|
-
}): react.JSX.Element;
|
|
10
|
-
declare function InfoRow({ label, value, copyable, }: {
|
|
11
|
-
label: string;
|
|
12
|
-
value: string;
|
|
13
|
-
copyable?: boolean;
|
|
14
|
-
}): react.JSX.Element;
|
|
15
|
-
declare function ShortcutRow({ keys, label }: {
|
|
16
|
-
keys: string[];
|
|
17
|
-
label: string;
|
|
18
|
-
}): react.JSX.Element;
|
|
19
|
-
declare function LoadingSpinner(): react.JSX.Element;
|
|
20
|
-
|
|
21
|
-
export { InfoRow, LoadingSpinner, PanelSection, ShortcutRow };
|
package/panels/info-row.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
import { HudSeedDescriptor } from '../core/api-types.mjs';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Runtime mock toggle + data seeding. The mock switch writes the `hud-mock`
|
|
6
|
-
* cookie and reloads — the server layout re-reads it and passes `forceMock`
|
|
7
|
-
* into the app's ConvexClientProvider, so SSR and client always agree. Seeds
|
|
8
|
-
* run on the dev machine through the bridge (`bunx convex run`), which keeps
|
|
9
|
-
* remote-refusal guardrails in seed functions intact.
|
|
10
|
-
*/
|
|
11
|
-
declare function MockSeedPanel({ seeds }: {
|
|
12
|
-
seeds?: HudSeedDescriptor[];
|
|
13
|
-
}): react.JSX.Element;
|
|
14
|
-
|
|
15
|
-
export { MockSeedPanel };
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MockSeedPanel
|
|
3
|
-
} from "../chunk-k0vr8dsy.js";
|
|
4
|
-
import"../chunk-50pq4sg6.js";
|
|
5
|
-
import"../chunk-wspz08q7.js";
|
|
6
|
-
import"../chunk-kyeb6qeq.js";
|
|
7
|
-
import"../chunk-xztw0xyt.js";
|
|
8
|
-
import"../chunk-hc2g0n35.js";
|
|
9
|
-
import"../chunk-babjpqkb.js";
|
|
10
|
-
import"../chunk-v086j53b.js";
|
|
11
|
-
import"../chunk-5gtx3pza.js";
|
|
12
|
-
export {
|
|
13
|
-
MockSeedPanel
|
|
14
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
|
|
3
|
-
interface HudRoleDescriptor {
|
|
4
|
-
id: string;
|
|
5
|
-
label: string;
|
|
6
|
-
/** Hierarchy level shown as "Lvl n"; omit to hide. */
|
|
7
|
-
level?: number;
|
|
8
|
-
/** Badge classes for the current-role chip (tone is app-specific). */
|
|
9
|
-
badgeClassName?: string;
|
|
10
|
-
}
|
|
11
|
-
interface HudRoleAdapter {
|
|
12
|
-
roles: HudRoleDescriptor[];
|
|
13
|
-
currentRoleId?: string;
|
|
14
|
-
currentUserLabel?: string;
|
|
15
|
-
isLoading?: boolean;
|
|
16
|
-
/** Perform the switch (Convex mutation, dev-login POST, …). May throw. */
|
|
17
|
-
onSwitch: (roleId: string) => Promise<void> | void;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Role switcher over an app-provided adapter. Harvested from nexus-lms'
|
|
21
|
-
* dev toolbar; the Convex mutation moved behind `onSwitch` so any auth
|
|
22
|
-
* backend (or dev-routes' `useDevRoleLogin`) can drive it.
|
|
23
|
-
*/
|
|
24
|
-
declare function RoleSwitcherPanel({ adapter }: {
|
|
25
|
-
adapter: HudRoleAdapter;
|
|
26
|
-
}): react.JSX.Element;
|
|
27
|
-
|
|
28
|
-
export { type HudRoleAdapter, type HudRoleDescriptor, RoleSwitcherPanel };
|
package/panels/sessions-panel.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SessionsPanel
|
|
3
|
-
} from "../chunk-t9eh4t0b.js";
|
|
4
|
-
import"../chunk-50pq4sg6.js";
|
|
5
|
-
import"../chunk-wspz08q7.js";
|
|
6
|
-
import"../chunk-kyeb6qeq.js";
|
|
7
|
-
import"../chunk-xztw0xyt.js";
|
|
8
|
-
import"../chunk-babjpqkb.js";
|
|
9
|
-
import"../chunk-v086j53b.js";
|
|
10
|
-
import"../chunk-5gtx3pza.js";
|
|
11
|
-
export {
|
|
12
|
-
SessionsPanel
|
|
13
|
-
};
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
|
|
3
|
-
interface HudTokenDescriptor {
|
|
4
|
-
/** CSS custom property, including the leading dashes (e.g. --bauhaus-primary-ink). */
|
|
5
|
-
cssVar: string;
|
|
6
|
-
/** Short display label; defaults to the var name without its prefix. */
|
|
7
|
-
label?: string;
|
|
8
|
-
}
|
|
9
|
-
interface HudTokenGroup {
|
|
10
|
-
label: string;
|
|
11
|
-
tokens: HudTokenDescriptor[];
|
|
12
|
-
/**
|
|
13
|
-
* Scope attribute the design system hangs its tokens on, with optional
|
|
14
|
-
* value — e.g. { attribute: "data-bauhaus" } or
|
|
15
|
-
* { attribute: "data-direction", value: "glass-vercel" }. Rendered onto the
|
|
16
|
-
* group's swatch container so scoped var() lookups resolve. Omit for
|
|
17
|
-
* :root-scoped systems.
|
|
18
|
-
*/
|
|
19
|
-
scope?: {
|
|
20
|
-
attribute: string;
|
|
21
|
-
value?: string;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
interface HudTokensAdapter {
|
|
25
|
-
groups: HudTokenGroup[];
|
|
26
|
-
/** Where the tokens live, shown as a hint (e.g. packages/ui/styles/bauhaus-tokens.css). */
|
|
27
|
-
sourceHint?: string;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Live design-token swatches: each tile paints `var(<token>)` inside the
|
|
31
|
-
* group's scope attribute, so what renders IS the resolved value in the
|
|
32
|
-
* current theme — no hardcoded copies to drift. Click copies the var name.
|
|
33
|
-
*/
|
|
34
|
-
declare function TokensPanel({ adapter }: {
|
|
35
|
-
adapter: HudTokensAdapter;
|
|
36
|
-
}): react.JSX.Element;
|
|
37
|
-
|
|
38
|
-
export { type HudTokenDescriptor, type HudTokenGroup, type HudTokensAdapter, TokensPanel };
|
package/panels/tokens-panel.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
import { HudAccess } from '../core/access.mjs';
|
|
3
|
-
import { HudEnv } from '../core/types.mjs';
|
|
4
|
-
import 'lucide-react';
|
|
5
|
-
|
|
6
|
-
type HudUnlockState = "checking" | "locked" | "unlocked" | "unconfigured";
|
|
7
|
-
/**
|
|
8
|
-
* Unlock state for a deployed hud.
|
|
9
|
-
*
|
|
10
|
-
* The browser never sees the password or the token: the cookie is httpOnly
|
|
11
|
-
* and only the server can judge it, so this hook asks `${apiBase}/unlock`
|
|
12
|
-
* rather than reading anything locally. On a tier that needs no unlock it
|
|
13
|
-
* settles on `unlocked` without a request.
|
|
14
|
-
*/
|
|
15
|
-
declare function useHudUnlock(apiBase: string, access: HudAccess): {
|
|
16
|
-
state: HudUnlockState;
|
|
17
|
-
error: string | null;
|
|
18
|
-
unlock: (password: string) => Promise<boolean>;
|
|
19
|
-
};
|
|
20
|
-
interface HudUnlockPanelProps {
|
|
21
|
-
env: HudEnv;
|
|
22
|
-
state: HudUnlockState;
|
|
23
|
-
error: string | null;
|
|
24
|
-
onUnlock: (password: string) => Promise<boolean>;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* The only panel a locked deployed hud shows. Everything else, including
|
|
28
|
-
* the read-only production panels, stays behind it, because the panels
|
|
29
|
-
* themselves fetch from `/api/hud/*` routes that would refuse anyway.
|
|
30
|
-
*/
|
|
31
|
-
declare function HudUnlockPanel({ env, state, error, onUnlock }: HudUnlockPanelProps): react.JSX.Element;
|
|
32
|
-
|
|
33
|
-
export { HudUnlockPanel, type HudUnlockPanelProps, type HudUnlockState, useHudUnlock };
|
package/panels/unlock-panel.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HudUnlockPanel,
|
|
3
|
-
useHudUnlock
|
|
4
|
-
} from "../chunk-msqkkqw7.js";
|
|
5
|
-
import"../chunk-r06bpmy6.js";
|
|
6
|
-
import"../chunk-kyeb6qeq.js";
|
|
7
|
-
import"../chunk-xztw0xyt.js";
|
|
8
|
-
import"../chunk-babjpqkb.js";
|
|
9
|
-
import"../chunk-v086j53b.js";
|
|
10
|
-
import"../chunk-5gtx3pza.js";
|
|
11
|
-
export {
|
|
12
|
-
useHudUnlock,
|
|
13
|
-
HudUnlockPanel
|
|
14
|
-
};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
|
|
3
|
-
interface HudVariantCategory {
|
|
4
|
-
id: string;
|
|
5
|
-
name: string;
|
|
6
|
-
/** Registry status — "active" renders as votable, "decided" as settled. */
|
|
7
|
-
status: string;
|
|
8
|
-
alternativeCount: number;
|
|
9
|
-
/** Domain bucket (marketing / admin / shared, …) for grouping. */
|
|
10
|
-
domain?: string;
|
|
11
|
-
}
|
|
12
|
-
interface HudGenerateRequest {
|
|
13
|
-
/** Repo-relative component path (apps/… or packages/…). */
|
|
14
|
-
path: string;
|
|
15
|
-
brief: string;
|
|
16
|
-
count: number;
|
|
17
|
-
}
|
|
18
|
-
interface HudGenerateResult {
|
|
19
|
-
ok: boolean;
|
|
20
|
-
/** Repo-relative queue marker path on success. */
|
|
21
|
-
markerPath?: string;
|
|
22
|
-
error?: string;
|
|
23
|
-
}
|
|
24
|
-
interface HudVariantsAdapter {
|
|
25
|
-
categories: HudVariantCategory[];
|
|
26
|
-
/** Deep link to the full review dashboard (e.g. /dev/review/console). */
|
|
27
|
-
reviewHref: string;
|
|
28
|
-
/** Deep link for one category's drill-in page. */
|
|
29
|
-
categoryHref?: (categoryId: string) => string;
|
|
30
|
-
/**
|
|
31
|
-
* Open the in-page preview drawer, when the app has a PreviewProvider
|
|
32
|
-
* mounted on the current route. Omit to fall back to deep links.
|
|
33
|
-
*/
|
|
34
|
-
openPreview?: (categoryId: string) => void;
|
|
35
|
-
/**
|
|
36
|
-
* Queue a variant-generation brief (console: /api/dev/generate-variants →
|
|
37
|
-
* .claude/queue marker — human stays in the loop, no autonomous dispatch).
|
|
38
|
-
*/
|
|
39
|
-
generate?: (request: HudGenerateRequest) => Promise<HudGenerateResult>;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Variant-voting overview over an app-provided adapter: registered preview
|
|
43
|
-
* categories with status, drill-in links (or the in-page drawer when
|
|
44
|
-
* mounted), and the generate-brief queue. Voting itself stays on the review
|
|
45
|
-
* dashboard — it needs the showcase to judge alternatives.
|
|
46
|
-
*/
|
|
47
|
-
declare function VariantsPanel({ adapter }: {
|
|
48
|
-
adapter: HudVariantsAdapter;
|
|
49
|
-
}): react.JSX.Element;
|
|
50
|
-
|
|
51
|
-
export { type HudGenerateRequest, type HudGenerateResult, type HudVariantCategory, type HudVariantsAdapter, VariantsPanel };
|
package/panels/variants-panel.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
VariantsPanel
|
|
3
|
-
} from "../chunk-k50yfp5d.js";
|
|
4
|
-
import"../chunk-wspz08q7.js";
|
|
5
|
-
import"../chunk-kyeb6qeq.js";
|
|
6
|
-
import"../chunk-xztw0xyt.js";
|
|
7
|
-
import"../chunk-babjpqkb.js";
|
|
8
|
-
import"../chunk-v086j53b.js";
|
|
9
|
-
import"../chunk-5gtx3pza.js";
|
|
10
|
-
export {
|
|
11
|
-
VariantsPanel
|
|
12
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
|
|
3
|
-
/** Current values as a plain record — consumed by the dispatch payload. */
|
|
4
|
-
declare function getVitalsSnapshot(): Record<string, number>;
|
|
5
|
-
/**
|
|
6
|
-
* Live Core Web Vitals of the current page (web-vitals, in-memory) plus the
|
|
7
|
-
* app's server-side SigNoz snapshot via the hud hub.
|
|
8
|
-
*/
|
|
9
|
-
declare function VitalsPanel(): react.JSX.Element;
|
|
10
|
-
|
|
11
|
-
export { VitalsPanel, getVitalsSnapshot };
|
package/panels/vitals-panel.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
VitalsPanel,
|
|
3
|
-
getVitalsSnapshot
|
|
4
|
-
} from "../chunk-ghxh8926.js";
|
|
5
|
-
import"../chunk-xezxydm3.js";
|
|
6
|
-
import"../chunk-wspz08q7.js";
|
|
7
|
-
import"../chunk-kyeb6qeq.js";
|
|
8
|
-
import"../chunk-xztw0xyt.js";
|
|
9
|
-
import"../chunk-babjpqkb.js";
|
|
10
|
-
import"../chunk-v086j53b.js";
|
|
11
|
-
import"../chunk-5gtx3pza.js";
|
|
12
|
-
export {
|
|
13
|
-
getVitalsSnapshot,
|
|
14
|
-
VitalsPanel
|
|
15
|
-
};
|
package/server/guard.d.mts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { HudEnv, HudCapability } from '../core/types.mjs';
|
|
2
|
-
import 'lucide-react';
|
|
3
|
-
import 'react';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Server-side hud gate: layer two of the three the hud architecture calls
|
|
7
|
-
* for (client render gate · per-app route gate · hub token).
|
|
8
|
-
*
|
|
9
|
-
* Client capability filtering is convenience: the panels a browser cannot see
|
|
10
|
-
* are still reachable by typing the URL. Every `/api/hud/*` route therefore
|
|
11
|
-
* re-derives the env itself and refuses anything the tier's matrix denies,
|
|
12
|
-
* which is what makes production genuinely read-only rather than
|
|
13
|
-
* read-only-by-UI. Deliberately framework-free (it takes anything with
|
|
14
|
-
* `headers.get`) so the route decides how to render the failure.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
interface HudGuardRequest {
|
|
18
|
-
headers: {
|
|
19
|
-
get(name: string): string | null;
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
type HudGuardResult = {
|
|
23
|
-
ok: true;
|
|
24
|
-
env: HudEnv;
|
|
25
|
-
} | {
|
|
26
|
-
ok: false;
|
|
27
|
-
status: 401 | 403 | 404;
|
|
28
|
-
error: string;
|
|
29
|
-
};
|
|
30
|
-
/**
|
|
31
|
-
* Whether this request may exercise `capability` right now.
|
|
32
|
-
*
|
|
33
|
-
* Order matters: a hud that is not enabled at all answers 404 (nothing here),
|
|
34
|
-
* a denied capability answers 403 before any password is considered (the
|
|
35
|
-
* password must never be a way to widen the matrix), and only a permitted
|
|
36
|
-
* capability gets as far as the unlock check.
|
|
37
|
-
*/
|
|
38
|
-
declare function checkHudRequest(request: HudGuardRequest, capability: HudCapability): Promise<HudGuardResult>;
|
|
39
|
-
/** Whether the current request already carries a valid unlock (status route). */
|
|
40
|
-
declare function isHudRequestUnlocked(request: HudGuardRequest): Promise<boolean>;
|
|
41
|
-
|
|
42
|
-
export { type HudGuardRequest, type HudGuardResult, checkHudRequest, isHudRequestUnlocked };
|
package/server/guard.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
checkHudRequest,
|
|
3
|
-
isHudRequestUnlocked
|
|
4
|
-
} from "../chunk-1atxwdhw.js";
|
|
5
|
-
import"../chunk-r06bpmy6.js";
|
|
6
|
-
import"../chunk-babjpqkb.js";
|
|
7
|
-
import"../chunk-v086j53b.js";
|
|
8
|
-
import"../chunk-5gtx3pza.js";
|
|
9
|
-
export {
|
|
10
|
-
isHudRequestUnlocked,
|
|
11
|
-
checkHudRequest
|
|
12
|
-
};
|
package/server/unlock.d.mts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `/api/hud/unlock` handler, shared by every app that mounts the hud.
|
|
3
|
-
*
|
|
4
|
-
* Framework-free on purpose (it takes anything with `headers.get` and a
|
|
5
|
-
* `json()`), so each app's route file is four lines of Next glue and the
|
|
6
|
-
* policy lives in one place. Verification is HMAC over the token's own issue
|
|
7
|
-
* time (see core/access.ts), so nothing server-side needs to be stored.
|
|
8
|
-
*/
|
|
9
|
-
interface HudUnlockRequest {
|
|
10
|
-
headers: {
|
|
11
|
-
get(name: string): string | null;
|
|
12
|
-
};
|
|
13
|
-
json(): Promise<unknown>;
|
|
14
|
-
}
|
|
15
|
-
interface HudUnlockResult {
|
|
16
|
-
status: number;
|
|
17
|
-
body: Record<string, unknown>;
|
|
18
|
-
/** `Set-Cookie` value, when the attempt changed the unlock state. */
|
|
19
|
-
setCookie?: string;
|
|
20
|
-
}
|
|
21
|
-
/** GET: what the client needs to decide between the panels and the lock. */
|
|
22
|
-
declare function handleHudUnlockStatus(request: HudUnlockRequest): Promise<HudUnlockResult>;
|
|
23
|
-
/** POST: exchange the password for a signed, expiring unlock cookie. */
|
|
24
|
-
declare function handleHudUnlockAttempt(request: HudUnlockRequest): Promise<HudUnlockResult>;
|
|
25
|
-
/** Test seam: the attempt budget is process state, not request state. */
|
|
26
|
-
declare function resetHudUnlockAttempts(): void;
|
|
27
|
-
|
|
28
|
-
export { type HudUnlockRequest, type HudUnlockResult, handleHudUnlockAttempt, handleHudUnlockStatus, resetHudUnlockAttempts };
|
package/server/unlock.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isHudRequestUnlocked
|
|
3
|
-
} from "../chunk-1atxwdhw.js";
|
|
4
|
-
import {
|
|
5
|
-
hudRequiresUnlock,
|
|
6
|
-
hudUnlockCookieString,
|
|
7
|
-
hudUnlockTtlMs,
|
|
8
|
-
resolveHudAccess,
|
|
9
|
-
signHudUnlockToken,
|
|
10
|
-
timingSafeEqual
|
|
11
|
-
} from "../chunk-r06bpmy6.js";
|
|
12
|
-
import {
|
|
13
|
-
isHudVisible,
|
|
14
|
-
resolveHudEnv
|
|
15
|
-
} from "../chunk-babjpqkb.js";
|
|
16
|
-
import"../chunk-v086j53b.js";
|
|
17
|
-
import"../chunk-5gtx3pza.js";
|
|
18
|
-
|
|
19
|
-
// server/unlock.ts
|
|
20
|
-
var ATTEMPT_WINDOW_MS = 10 * 60 * 1000;
|
|
21
|
-
var MAX_FAILED_ATTEMPTS = 10;
|
|
22
|
-
var failures = new Map;
|
|
23
|
-
function clientKey(request) {
|
|
24
|
-
const forwarded = request.headers.get("x-forwarded-for");
|
|
25
|
-
return forwarded?.split(",")[0]?.trim() || request.headers.get("x-real-ip") || "unknown";
|
|
26
|
-
}
|
|
27
|
-
function isThrottled(key, now) {
|
|
28
|
-
const entry = failures.get(key);
|
|
29
|
-
if (!entry)
|
|
30
|
-
return false;
|
|
31
|
-
if (now - entry.firstAt > ATTEMPT_WINDOW_MS) {
|
|
32
|
-
failures.delete(key);
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
return entry.count >= MAX_FAILED_ATTEMPTS;
|
|
36
|
-
}
|
|
37
|
-
function recordFailure(key, now) {
|
|
38
|
-
const entry = failures.get(key);
|
|
39
|
-
if (!entry || now - entry.firstAt > ATTEMPT_WINDOW_MS) {
|
|
40
|
-
failures.set(key, { count: 1, firstAt: now });
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
entry.count += 1;
|
|
44
|
-
}
|
|
45
|
-
async function handleHudUnlockStatus(request) {
|
|
46
|
-
const env = resolveHudEnv();
|
|
47
|
-
if (!isHudVisible(env)) {
|
|
48
|
-
return { status: 404, body: { error: "not found" } };
|
|
49
|
-
}
|
|
50
|
-
const access = resolveHudAccess(env);
|
|
51
|
-
return {
|
|
52
|
-
status: 200,
|
|
53
|
-
body: {
|
|
54
|
-
env,
|
|
55
|
-
access,
|
|
56
|
-
required: hudRequiresUnlock(access),
|
|
57
|
-
configured: !hudRequiresUnlock(access) || Boolean(process.env.HUD_ACCESS_PASSWORD),
|
|
58
|
-
unlocked: await isHudRequestUnlocked(request)
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
async function handleHudUnlockAttempt(request) {
|
|
63
|
-
const env = resolveHudEnv();
|
|
64
|
-
if (!isHudVisible(env)) {
|
|
65
|
-
return { status: 404, body: { error: "not found" } };
|
|
66
|
-
}
|
|
67
|
-
const access = resolveHudAccess(env);
|
|
68
|
-
if (!hudRequiresUnlock(access)) {
|
|
69
|
-
return { status: 200, body: { unlocked: true, required: false } };
|
|
70
|
-
}
|
|
71
|
-
const expected = process.env.HUD_ACCESS_PASSWORD;
|
|
72
|
-
if (!expected) {
|
|
73
|
-
return {
|
|
74
|
-
status: 503,
|
|
75
|
-
body: { unlocked: false, configured: false, error: "hud unlock is not configured" }
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
const now = Date.now();
|
|
79
|
-
const key = clientKey(request);
|
|
80
|
-
if (isThrottled(key, now)) {
|
|
81
|
-
return { status: 429, body: { unlocked: false, error: "too many attempts, try again later" } };
|
|
82
|
-
}
|
|
83
|
-
let submitted = "";
|
|
84
|
-
try {
|
|
85
|
-
const payload = await request.json();
|
|
86
|
-
if (typeof payload?.password === "string")
|
|
87
|
-
submitted = payload.password;
|
|
88
|
-
} catch {}
|
|
89
|
-
if (!submitted || !timingSafeEqual(submitted, expected)) {
|
|
90
|
-
recordFailure(key, now);
|
|
91
|
-
return { status: 401, body: { unlocked: false, error: "wrong password" } };
|
|
92
|
-
}
|
|
93
|
-
failures.delete(key);
|
|
94
|
-
const ttlMs = hudUnlockTtlMs(access);
|
|
95
|
-
return {
|
|
96
|
-
status: 200,
|
|
97
|
-
body: { unlocked: true, env, access, expiresInMs: ttlMs },
|
|
98
|
-
setCookie: hudUnlockCookieString(await signHudUnlockToken(expected, now), ttlMs)
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
function resetHudUnlockAttempts() {
|
|
102
|
-
failures.clear();
|
|
103
|
-
}
|
|
104
|
-
export {
|
|
105
|
-
resetHudUnlockAttempts,
|
|
106
|
-
handleHudUnlockStatus,
|
|
107
|
-
handleHudUnlockAttempt
|
|
108
|
-
};
|