@teacss/inspector 0.9.3 → 0.10.2

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 CHANGED
@@ -1,25 +1,21 @@
1
1
  # @teacss/inspector
2
2
 
3
- Development inspector for TeaCSS Vite projects.
3
+ The TeaCSS inspector: a page on the development server that shows the resolved project, each module's tokens, the generated CSS and a REPL with completion. Token lookup is an endpoint, not a view: `GET /__teacss/api/token/<token>` (token percent-encoded) returns one token's match info as JSON. It reads the Rust engine (`@teacss/engine`) and answers loopback requests only.
4
+
5
+ You do not install it yourself. `@teacss/vite`, and so `@teacss/astro`, depends on it and serves it at `/__teacss` on the dev server. It never runs in a build. Pass `inspector: false` to the plugin to turn it off:
4
6
 
5
7
  ```ts
8
+ // vite.config.ts
9
+ import { teacss } from "@teacss/vite";
6
10
  import { defineConfig } from "vite";
7
- import { pluginInspector } from "@teacss/inspector";
8
- import { pluginTeacss } from "@teacss/vite";
9
11
 
10
- export default defineConfig({
11
- plugins: [pluginTeacss(), pluginInspector()],
12
- });
12
+ export default defineConfig({ plugins: [teacss({ inspector: false })] });
13
13
  ```
14
14
 
15
- Start Vite and open `http://localhost:5173/__teacss/` to inspect the active
16
- project, scanned modules, generated CSS, and REPL output.
17
-
18
- Inspector APIs return project data and module source only when both the request
19
- host and actual socket peer are loopback. Exposing Vite on a network interface
20
- does not expose this data remotely.
15
+ The package exports `createInspectorMiddleware(project)`, a Connect-compatible middleware over a `Project` from `@teacss/engine`, for hosts that mount it themselves.
21
16
 
22
- The inspector observes the Vite plugin's live context and does not generate
23
- production CSS or provide editor features.
17
+ ## Documentation
24
18
 
25
- Pre-1.0.
19
+ - [The inspector](https://github.com/teacss/teacss-rs/blob/master/skills/teacss/references/tooling.md#inspector)
20
+ - [Build integrations](https://github.com/teacss/teacss-rs/blob/master/skills/teacss/references/integrations.md)
21
+ - [The TeaCSS skill](https://github.com/teacss/teacss-rs/blob/master/skills/teacss/SKILL.md) for syntax, presets and tooling
@@ -0,0 +1,2 @@
1
+ export declare function isLoopbackRequest(hostHeader: string | undefined, remoteAddress: string | undefined): boolean;
2
+ //# sourceMappingURL=access.d.ts.map
package/dist/access.js ADDED
@@ -0,0 +1,30 @@
1
+ // The inspector answers only loopback peers that also name a loopback host: the Host header alone is
2
+ // client-controlled, and `vite --host` would otherwise hand the config, module tokens and REPL to the LAN.
3
+ import { BlockList, isIP } from "node:net";
4
+ const loopback = new BlockList();
5
+ loopback.addSubnet("127.0.0.0", 8, "ipv4");
6
+ loopback.addAddress("::1", "ipv6");
7
+ function isLoopbackAddress(value) {
8
+ if (!value)
9
+ return false;
10
+ const address = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
11
+ const family = isIP(address);
12
+ return family !== 0 && loopback.check(address, family === 4 ? "ipv4" : "ipv6");
13
+ }
14
+ /** The hostname of a `Host` header (`name`, `name:port`, `[v6]`, `[v6]:port`); undefined when malformed. */
15
+ function hostnameOf(header) {
16
+ const value = header?.trim().toLowerCase();
17
+ if (!value)
18
+ return undefined;
19
+ if (isIP(value))
20
+ return value;
21
+ const match = value.startsWith("[") ? /^\[([^\]]+)\](?::(\d+))?$/.exec(value) : /^([^:]+)(?::(\d+))?$/.exec(value);
22
+ if (!match || (match[2] !== undefined && Number(match[2]) > 65_535))
23
+ return undefined;
24
+ return value.startsWith("[") ? match[1] : match[1]?.replace(/\.$/, "");
25
+ }
26
+ export function isLoopbackRequest(hostHeader, remoteAddress) {
27
+ const hostname = hostnameOf(hostHeader);
28
+ return (hostname === "localhost" || isLoopbackAddress(hostname)) && isLoopbackAddress(remoteAddress);
29
+ }
30
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ :root{--bg: #0f1115;--panel: #161a21;--panel-2: #1c2129;--border: #2a3038;--text: #d7dce3;--muted: #8b95a3;--accent: #3ddc97;--accent-2: #5b9dff;--radius: 8px;color-scheme:dark}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif}code,pre,.token,.css{font-family:ui-monospace,SF Mono,JetBrains Mono,Menlo,monospace}.muted{color:var(--muted)}.small{font-size:12px}.ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.app{display:grid;grid-template-columns:280px 1fr;height:100vh}.sidebar{border-right:1px solid var(--border);background:var(--panel);display:flex;flex-direction:column;padding:16px;gap:16px;overflow:hidden}.content{overflow:auto;padding:24px 32px}.brand{display:flex;align-items:center;gap:10px}.brand .logo{font-size:26px}nav{display:flex;flex-direction:column;gap:2px}.navlink{display:block;padding:7px 10px;border-radius:6px;color:var(--text);text-decoration:none}.navlink:hover{background:var(--panel-2)}.navlink.active{background:var(--panel-2);color:var(--accent)}.side-meta{display:flex;flex-direction:column;gap:10px;padding-bottom:4px;border-bottom:1px solid var(--border)}.modules{overflow:auto;flex:1}.modules-head{position:sticky;top:0;background:var(--panel);padding:4px 0}.modules ul{list-style:none;margin:0;padding:0}.modules a{display:block;padding:4px 8px;border-radius:5px;color:var(--muted);text-decoration:none;font-size:12px;font-family:ui-monospace,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.modules a:hover{color:var(--text);background:var(--panel-2)}.modules a.active{color:var(--accent-2);background:var(--panel-2)}.view-head{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:20px}.view-head h1{font-size:20px;margin:0}section{margin-bottom:28px}section h2{font-size:14px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin:0 0 10px}button,.toggle{background:var(--panel-2);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:6px 12px;cursor:pointer;font:inherit}button:hover{border-color:var(--accent)}.toggle{display:inline-flex;align-items:center;gap:6px}.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:12px;margin-bottom:28px}.stat{background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);padding:14px 16px}.stat-value{font-size:22px;font-weight:600;color:var(--accent)}.stat-label{color:var(--muted);font-size:12px}.layers{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:8px}.layers li{display:flex;gap:8px;align-items:center;background:var(--panel);border:1px solid var(--border);border-radius:6px;padding:6px 10px}.filter{width:100%;margin-bottom:10px;padding:8px 10px;background:var(--panel);border:1px solid var(--border);border-radius:6px;color:var(--text);font:inherit}table.matched{width:100%;border-collapse:collapse}table.matched th,table.matched td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--border);vertical-align:top}table.matched th{color:var(--muted);font-weight:500;font-size:12px}.num{text-align:right;font-variant-numeric:tabular-nums}.token{color:var(--accent-2)}.css{color:var(--muted);white-space:pre-wrap;word-break:break-word}.tag{background:var(--panel-2);border-radius:4px;padding:1px 6px;font-size:12px}.modlink{color:var(--muted);margin-right:6px;text-decoration:none}.modlink:hover{color:var(--accent-2)}.code{background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);padding:14px 16px;overflow:auto;white-space:pre-wrap;word-break:break-word;font-size:12.5px;max-height:60vh}.code.source{white-space:pre}.repl-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;height:calc(100vh - 130px)}.repl-input{background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);padding:14px 16px;resize:none;font-family:ui-monospace,monospace;font-size:13px}.repl-editor{display:flex;flex-direction:column;gap:8px;min-height:0}.repl-editor .repl-input{flex:1}.suggest{list-style:none;margin:0;padding:4px;background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);max-height:200px;overflow:auto;font-family:ui-monospace,monospace;font-size:12.5px}.suggest li{display:flex;justify-content:space-between;gap:12px;padding:4px 8px;border-radius:4px;cursor:pointer}.suggest li.active,.suggest li:hover{background:var(--border)}.suggest .muted{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.repl-output{display:flex;flex-direction:column;gap:8px;overflow:hidden}.repl-output .code{flex:1;max-height:none}.state{color:var(--muted);padding:40px 0}.state.error{color:#ff6b6b}