@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.
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>TeaCSS Inspector</title>
7
- <script type="module" crossorigin src="/__teacss/assets/index-BMcqNdMD.js"></script>
8
- <link rel="stylesheet" crossorigin href="/__teacss/assets/index-QRwAp_3w.css">
7
+ <script type="module" crossorigin src="/__teacss/assets/index-DHyQVuSN.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/__teacss/assets/index-BsirYgOp.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="app"></div>
package/dist/index.d.ts CHANGED
@@ -1,115 +1,12 @@
1
- import { TeacssGenerator, TeacssPluginContext } from "@teacss/core";
2
- import { IncomingMessage, ServerResponse } from "node:http";
3
- import { Plugin } from "vite";
4
- //#region src/types.d.ts
5
- /** A single matched token (utility/class) and what it produced. */
6
- export interface MatchedToken {
7
- /** The raw token as written in source, e.g. `bg-color:red-500`. */
8
- token: string;
9
- /** The generated CSS declarations (rule bodies joined), for preview. */
10
- css: string;
11
- /** How many times the token resolved across the analyzed input. */
12
- count: number;
13
- /** A coarse family used to group tokens in the UI (the property prefix). */
14
- category: string;
15
- /** Source module ids that contained this token (empty for the REPL). */
16
- modules: string[];
17
- }
18
- /** One output layer and its CSS. */
19
- export interface LayerCss {
20
- name: string;
21
- css: string;
22
- }
23
- /** The common payload behind /overview, /module and /repl. */
24
- export interface GenerateResultDTO {
25
- /** Full generated CSS (all layers concatenated in order). */
26
- css: string;
27
- /** Per-layer CSS, in layer order. */
28
- layers: LayerCss[];
29
- /** Matched tokens, sorted by descending count. */
30
- matched: MatchedToken[];
31
- }
32
- /** A safe, acyclic summary of the resolved config (no live objects). */
33
- export interface ConfigSummary {
34
- presets: string[];
35
- /** `[name, order]` pairs, sorted by order. */
36
- layers: [string, number][];
37
- rules: number;
38
- shortcuts: number;
39
- themeKeys: string[];
40
- }
41
- /** GET / — project + config overview chrome. */
42
- export interface ProjectInfo {
43
- version: string;
44
- root: string;
45
- /** Scanned module ids (sorted). */
46
- modules: string[];
47
- /** Config entry/source files backing the resolved config. */
48
- configSources: string[];
49
- config: ConfigSummary;
50
- }
51
- /** GET /overview — whole-project generated CSS + analysis. */
52
- export interface OverviewInfo extends GenerateResultDTO {
53
- gzipSize: number;
54
- }
55
- /** GET /module?id= — a single module's source + what it generated. */
56
- export interface ModuleInfo extends GenerateResultDTO {
57
- id: string;
58
- code: string;
59
- gzipSize: number;
60
- }
61
- /** GET /repl?token= — generate CSS from arbitrary input. */
62
- export type ReplResult = GenerateResultDTO;
63
- /** Mount paths (also used to build client asset URLs). */
64
- export declare const CLIENT_BASE = "__teacss";
65
- export declare const API_BASE = "__teacss_api";
66
- //#endregion
67
- //#region src/analyzer.d.ts
68
- /** Map of source-module id → the tokens extracted from it. */
69
- type ModuleTokens = Map<string, Set<string>>;
70
- /** Coarse grouping family for a token: its property prefix, else the keyword stem. */
71
- export declare function categoryOf(token: string): string;
72
- interface GenerateDTOOptions {
73
- /** Module attribution source; when given, each matched token lists its files. */
74
- moduleTokens?: ModuleTokens;
75
- /** Apply the config safelist (REPL toggle). */
76
- safelist?: boolean;
77
- /** Module id for scoped generation. */
78
- id?: string;
79
- }
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import { type Project } from "@teacss/engine";
3
+ export type { ModuleTokens, ProjectResponse, ReplRequest } from "./types.js";
4
+ /** Connect-compatible: `server.middlewares.use("/__teacss", createInspectorMiddleware(project))`. */
5
+ export type InspectorMiddleware = (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
80
6
  /**
81
- * Run the generator over `input` (a token set or a raw string the generator
82
- * auto-extracts) and shape the result into the inspector's wire DTO: full CSS,
83
- * per-layer CSS, and the matched tokens with declaration bodies, counts, and
84
- * (optionally) which modules used each.
7
+ * v10.2 §3.6: `/api/project`, `/api/modules`, `/api/css`, `/api/stats` and `POST /api/repl` read `project` through napi;
8
+ * v10.3 §5 adds `/api/token/<token>` and `/api/suggest?q=`, and `/api/module?id=` generates one module's token list.
9
+ * Every other path is the static UI. Only loopback requests are answered (403 otherwise).
85
10
  */
86
- export declare function generateDTO(generator: TeacssGenerator, input: Set<string> | string, options?: GenerateDTOOptions): Promise<GenerateResultDTO>;
87
- //#endregion
88
- //#region src/index.d.ts
89
- /** One scanned module the inspector tracks: its source + the tokens it used. */
90
- interface ModuleEntry {
91
- code: string;
92
- tokens: Set<string>;
93
- }
94
- /** The inspector's per-module state, shared between the plugin and the API. */
95
- export interface InspectorStore {
96
- /** Clean id (no `?query`) → entry. */
97
- modules: Map<string, ModuleEntry>;
98
- /** Bumped whenever `modules` or the config changes — keys the overview cache. */
99
- version: () => number;
100
- }
101
- /**
102
- * A connect-style request handler serving the inspector's JSON API. Reads the
103
- * live generator/context plus the per-module `store` the plugin tracks. Mount it
104
- * under `/${API_BASE}`. The `/overview` response is cached on the store version.
105
- */
106
- export declare function createInspectorApi(ctx: TeacssPluginContext, store: InspectorStore): (req: IncomingMessage, res: ServerResponse, next: () => void) => Promise<void>;
107
- /**
108
- * Vite plugin: mounts the TeaCSS inspector on the dev server. Add it alongside
109
- * `pluginTeacss()` — it discovers the live context through that plugin's `teacss:api`
110
- * and serves the UI at `/${CLIENT_BASE}` with its JSON API at `/${API_BASE}`.
111
- * Dev-only (`apply: "serve"`).
112
- */
113
- export declare function pluginInspector(): Plugin;
114
- //#endregion
115
- export type { ModuleTokens };
11
+ export declare function createInspectorMiddleware(project: Project): InspectorMiddleware;
12
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,2 +1,132 @@
1
- import{fileURLToPath as e}from"node:url";import{gzipSync as t}from"node:zlib";import{SKIP_END_COMMENT as n,SKIP_START_COMMENT as r}from"@teacss/core";import i from"sirv";import{BlockList as a,isIP as o}from"node:net";const s=new a;s.addSubnet(`127.0.0.0`,8,`ipv4`),s.addAddress(`::1`,`ipv6`);function c(e){if(!e)return!1;let t=e.startsWith(`[`)&&e.endsWith(`]`)?e.slice(1,-1):e,n=o(t);return n?s.check(t,n===4?`ipv4`:`ipv6`):!1}function l(e){if(!e)return;let t=e.trim().toLowerCase();if(!t)return;if(o(t))return t;if(t.startsWith(`[`)){let e=t.match(/^\[([^\]]+)\](?::(\d+))?$/);return!e||!u(e[2])?void 0:e[1]}let n=t.match(/^([^:]+)(?::(\d+))?$/);if(n&&u(n[2]))return n[1].replace(/\.$/,``)}function u(e){return e===void 0||Number(e)<=65535}function d(e,t){let n=l(e);return(n===`localhost`||c(n))&&c(t)}function f(e){let t=e.indexOf(`@`),n=t>=0?e.slice(0,t):e,r=n.indexOf(`:`);if(r>=0)return n.slice(0,r);let i=n.indexOf(`-`);return(i>=0?n.slice(0,i):n)||`misc`}function p(e,t){let n=new Map;if(!t.size)return n;for(let[r,i]of e)for(let e of i){if(!t.has(e))continue;let i=n.get(e);i||(i=[],n.set(e,i)),i.push(r)}for(let e of n.values())e.length>1&&e.sort();return n}function m(e){let t=new Set;for(let n of e.values())for(let e of n)t.add(e);return t}async function h(e,t,n={}){let r=await e.generate(t,{extendedInfo:!0,preflights:!1,safelist:n.safelist??!1,id:n.id}),i=n.moduleTokens?p(n.moduleTokens,r.matched):void 0,a=[];for(let[e,t]of r.matched){let n=``;for(let e of t.data){let t=e[2];t&&(n+=n?` ${t}`:t)}let r=i?.get(e)??[];a.push({token:e,css:n,count:i?r.length:1,category:f(e),modules:r})}a.sort((e,t)=>t.count-e.count||e.token.localeCompare(t.token));let o=r.layers.map(e=>({name:e,css:r.getLayer(e)??``}));return{css:r.css,layers:o,matched:a}}const g=`__teacss`,_=`__teacss_api`,v=e=>e.split(`?`)[0],y=e=>e.replace(/([?&])(?:t|v)=[^&]+$/,``).replace(/\?$/,``);function b(e){let t=new Map;for(let[n,r]of e)t.set(n,r.tokens);return t}const x=RegExp(`(//\\s*?${r}\\s*?|\\/\\*\\s*?${r}\\s*?\\*\\/|<!--\\s*?${r}\\s*?-->)[\\s\\S]*?(//\\s*?${n}\\s*?|\\/\\*\\s*?${n}\\s*?\\*\\/|<!--\\s*?${n}\\s*?-->)`,`g`);function S(e){return!e.includes(r)||!e.includes(n)?e:e.replace(x,``)}async function C(e){await e[Symbol.for(`@teacss/engine/materialize`)]?.();let t=e.config;return{presets:t.presets.map(e=>e.name).filter(e=>!!e),layers:Object.entries(t.layers).sort((e,t)=>e[1]-t[1]),rules:t.rules.length,shortcuts:Array.isArray(t.shortcuts)?t.shortcuts.length:Object.keys(t.shortcuts).length,themeKeys:Object.keys(t.theme)}}async function w(e,t,n){return{version:t.version,root:e.root,modules:[...n.keys()].sort(),configSources:e.getConfigFileList?.()??[],config:await C(t)}}const T=e=>t(Buffer.from(e)).length;function E(e,t){let n;return async(r,i,a)=>{let o=(e,t=200)=>{i.statusCode=t,i.setHeader(`Content-Type`,`application/json`),i.end(JSON.stringify(e))};if(!d(r.headers.host,r.socket?.remoteAddress))return o({error:`forbidden`},403);try{let i=new URL(r.url??`/`,`http://localhost`);await e.ready;let s=e.generator,{modules:c}=t;switch(i.pathname){case`/`:case``:return o(await w(e,s,c));case`/overview`:{let r=t.version();if(n?.version===r)return o(n.data);let i=b(c),a=m(i);for(let t of e.tokens)a.add(t);let l=await h(s,a,{moduleTokens:i}),u={...l,gzipSize:T(l.css)};return n={version:r,data:u},o(u)}case`/repl`:{let e=i.searchParams.get(`token`)??``;return e.length>5e4?o({error:`input too large`},413):o(await h(s,e,{safelist:i.searchParams.get(`safelist`)===`true`}))}case`/module`:{let e=i.searchParams.get(`id`)??``,t=c.get(e);if(!t)return o({error:`module not found`},404);let n=await h(s,t.tokens,{id:e,moduleTokens:b(c)});return o({...n,id:e,code:t.code,gzipSize:T(n.css)})}default:return a()}}catch(e){o({error:e instanceof Error?e.message:String(e)},500)}}}function D(){let t=new Map,n=new Map,r=new Map,a=0,o=()=>{a++},s={modules:t,version:()=>a},c,l,u,d;function f(e){let r=n.get(e);if(!r?.size){n.delete(e),t.delete(e);return}if(r.size===1){t.set(e,r.values().next().value);return}let i=new Set;for(let e of r.values())for(let t of e.tokens)i.add(t);let a=r.get(e)?.code??[...r.values()].map(e=>e.code).join(`
2
- `);t.set(e,{code:a,tokens:i})}function p(e,t){return n.get(e)?.delete(t)?(f(e),!0):!1}function m(e){let i=v(e),a=y(e);if(a!==i){r.delete(a),p(i,a)&&o();return}for(let e of r.keys())v(e)===i&&r.delete(e);let s=n.delete(i),c=t.delete(i);(s||c)&&o()}function h(){d?.(),d=void 0,u?.watcher.off(`unlink`,m),u=void 0}function b(){r.clear();let e=t.size>0||n.size>0;t.clear(),n.clear(),e&&o()}function x(){h(),l?.(),l=void 0,b(),c=void 0}return{name:`teacss:inspector`,apply:`serve`,configResolved(e){l?.(),l=void 0;let t=(e.plugins.find(e=>e.name===`teacss:api`)?.api)?.getContext?.();c!==t&&(h(),b()),c=t,c?l=c.onInvalidate(o):e.logger.warn(`[teacss:inspector] no pluginTeacss() plugin found — the inspector is disabled.`)},async transform(e,t){let i=c;if(!i)return null;let a=v(t),s=y(t),l={};if(r.set(s,l),!i.filter(e,t))return r.delete(s),p(a,s)&&o(),null;try{await i.ready;let u=await i.generator.applyExtractors(S(e),t,new Set);if(c!==i||r.get(s)!==l)return null;if(u.size){let t=n.get(a);t||n.set(a,t=new Map),t.set(s,{code:e,tokens:u}),f(a),o()}else p(a,s)&&o();return null}finally{r.get(s)===l&&r.delete(s)}},async configureServer(t){let n=c;if(!n||(await n.ready,c!==n||u===t))return;if(h(),u=t,t.watcher.on(`unlink`,m),t.httpServer){let e=()=>{u===t&&x()};t.httpServer.once(`close`,e),d=()=>t.httpServer?.off(`close`,e)}let r=e(new URL(`./client`,import.meta.url));t.middlewares.use(`/${g}`,i(r,{single:!0,dev:!0})),t.middlewares.use(`/${_}`,E(n,s)),t.config.logger.info(`\n \x1b[32m➜\x1b[0m \x1b[36mTeaCSS Inspector\x1b[0m: \x1b[2mhttp://localhost:${t.config.server.port??5173}/${g}/\x1b[0m\n`)},closeBundle(){x()},devtools:{setup(e){e.docks.register({id:`teacss`,title:`TeaCSS`,icon:`mdi:tea`,type:`iframe`,url:`/${g}/`})}}}}export{_ as API_BASE,g as CLIENT_BASE,f as categoryOf,E as createInspectorApi,h as generateDTO,D as pluginInspector};
1
+ import { fileURLToPath } from "node:url";
2
+ import { version } from "@teacss/engine";
3
+ import sirv from "sirv";
4
+ import { isLoopbackRequest } from "./access.js";
5
+ /** A REPL body past this is refused before it is read into memory (the engine's own source bound is v02.2 §8's 16 MiB). */
6
+ const MAX_BODY_BYTES = 1 << 20;
7
+ const CLIENT_DIR = fileURLToPath(new URL("./client/", import.meta.url));
8
+ /** `GET /api/token/<token>`; the token is percent-encoded, so `rd:1/2` arrives as `rd%3A1%2F2`. */
9
+ const TOKEN_ROUTE = "/api/token/";
10
+ class HttpError extends Error {
11
+ status;
12
+ constructor(status, message) {
13
+ super(message);
14
+ this.status = status;
15
+ }
16
+ }
17
+ /** `/api/suggest` answers at most this many candidates; `limit` can lower it. */
18
+ const MAX_SUGGESTIONS = 100;
19
+ /**
20
+ * v10.2 §3.6: `/api/project`, `/api/modules`, `/api/css`, `/api/stats` and `POST /api/repl` read `project` through napi;
21
+ * v10.3 §5 adds `/api/token/<token>` and `/api/suggest?q=`, and `/api/module?id=` generates one module's token list.
22
+ * Every other path is the static UI. Only loopback requests are answered (403 otherwise).
23
+ */
24
+ export function createInspectorMiddleware(project) {
25
+ const ui = sirv(CLIENT_DIR, { single: true });
26
+ return (req, res, next) => {
27
+ const reply = ({ status, body }) => {
28
+ res.statusCode = status;
29
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
30
+ res.end(JSON.stringify(body));
31
+ };
32
+ if (!isLoopbackRequest(req.headers.host, req.socket.remoteAddress)) {
33
+ reply({ status: 403, body: { error: "inspector is loopback-only" } });
34
+ return;
35
+ }
36
+ const url = new URL(req.url ?? "/", "http://localhost");
37
+ if (!url.pathname.startsWith("/api/")) {
38
+ ui(req, res, next);
39
+ return;
40
+ }
41
+ route(project, req, url)
42
+ .catch((error) => ({ status: error instanceof HttpError ? error.status : 500, body: { error: error instanceof Error ? error.message : String(error) } }))
43
+ .then(reply);
44
+ };
45
+ }
46
+ async function route(project, req, url) {
47
+ const { engine } = project;
48
+ const { pathname } = url;
49
+ if (req.method === "GET" && pathname.startsWith(TOKEN_ROUTE)) {
50
+ const token = decodeToken(pathname.slice(TOKEN_ROUTE.length));
51
+ const info = engine.tokenInfo(token);
52
+ return info ? { status: 200, body: info } : { status: 404, body: { error: `no utility matches ${JSON.stringify(token)}` } };
53
+ }
54
+ switch (`${req.method} ${pathname}`) {
55
+ case "GET /api/project": {
56
+ const body = { version: version(), root: project.root, ...project.info, config: engine.configView() };
57
+ return { status: 200, body };
58
+ }
59
+ case "GET /api/modules": {
60
+ const body = project.modules().map((id) => ({ id, tokens: engine.moduleTokens(id) ?? [] }));
61
+ return { status: 200, body };
62
+ }
63
+ case "GET /api/module": {
64
+ const tokens = engine.moduleTokens(url.searchParams.get("id") ?? "");
65
+ if (tokens === null)
66
+ throw new HttpError(404, "no such module");
67
+ return { status: 200, body: engine.generate({ kind: "tokens", tokens }, { css: true, extendedInfo: true, preflights: false }) };
68
+ }
69
+ case "GET /api/css":
70
+ return { status: 200, body: project.css({ extendedInfo: true }) };
71
+ case "GET /api/stats":
72
+ return { status: 200, body: engine.stats() };
73
+ case "GET /api/suggest": {
74
+ const q = url.searchParams.get("q");
75
+ if (q === null)
76
+ throw new HttpError(400, "query needs q=<input>");
77
+ return { status: 200, body: engine.suggest(q, { limit: parseLimit(url.searchParams.get("limit")) }) };
78
+ }
79
+ case "POST /api/repl": {
80
+ const { code, safelist = false } = parseRepl(await readBody(req));
81
+ return { status: 200, body: engine.generate({ kind: "code", code }, { css: true, extendedInfo: true, preflights: false, safelist }) };
82
+ }
83
+ default:
84
+ return { status: 404, body: { error: `no route ${req.method} ${pathname}` } };
85
+ }
86
+ }
87
+ function readBody(req) {
88
+ return new Promise((resolve, reject) => {
89
+ const chunks = [];
90
+ let size = 0;
91
+ req.on("data", (chunk) => {
92
+ size += chunk.length;
93
+ if (size > MAX_BODY_BYTES)
94
+ reject(new HttpError(413, `body over ${MAX_BODY_BYTES} bytes`));
95
+ else
96
+ chunks.push(chunk);
97
+ });
98
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
99
+ req.on("error", reject);
100
+ });
101
+ }
102
+ function parseRepl(text) {
103
+ let parsed;
104
+ try {
105
+ parsed = JSON.parse(text);
106
+ }
107
+ catch {
108
+ throw new HttpError(400, "body is not JSON");
109
+ }
110
+ const { code, safelist } = (typeof parsed === "object" && parsed !== null ? parsed : {});
111
+ if (typeof code !== "string" || (safelist !== undefined && typeof safelist !== "boolean"))
112
+ throw new HttpError(400, 'body must be { "code": string, "safelist"?: boolean }');
113
+ return { code, safelist };
114
+ }
115
+ function decodeToken(encoded) {
116
+ try {
117
+ return decodeURIComponent(encoded);
118
+ }
119
+ catch {
120
+ throw new HttpError(400, "token is not percent-encoded");
121
+ }
122
+ }
123
+ /** `limit` as a count in 0–MAX_SUGGESTIONS; absent means the maximum. */
124
+ function parseLimit(raw) {
125
+ if (raw === null)
126
+ return MAX_SUGGESTIONS;
127
+ const limit = Number(raw);
128
+ if (!Number.isInteger(limit) || limit < 0)
129
+ throw new HttpError(400, "limit must be a non-negative integer");
130
+ return Math.min(limit, MAX_SUGGESTIONS);
131
+ }
132
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,18 @@
1
+ import type { LoadProjectFromRootResult, ResolvedConfigView } from "@teacss/engine";
2
+ /** GET /api/project: the engine version, the shell's root, the latest `loadProjectFromRoot` result and `configView()`. */
3
+ export interface ProjectResponse extends LoadProjectFromRootResult {
4
+ version: string;
5
+ root: string;
6
+ config: ResolvedConfigView;
7
+ }
8
+ /** GET /api/modules: one entry per module the shell registered, with the tokens the engine extracted from it. */
9
+ export interface ModuleTokens {
10
+ id: string;
11
+ tokens: string[];
12
+ }
13
+ /** POST /api/repl body; the answer is `generate({ kind: "code", code })` without preflights. `safelist` defaults to false. */
14
+ export interface ReplRequest {
15
+ code: string;
16
+ safelist?: boolean;
17
+ }
18
+ //# sourceMappingURL=types.d.ts.map
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,47 +1,60 @@
1
1
  {
2
2
  "name": "@teacss/inspector",
3
- "version": "0.9.3",
4
- "description": "The inspector UI for TeaCSS — browse generated CSS, modules, and a live REPL.",
5
- "homepage": "https://css.teasim.com",
6
- "funding": "https://github.com/sponsors/billgo",
3
+ "version": "0.10.2",
4
+ "description": "TeaCSS inspector: the dev-server page @teacss/vite mounts at /__teacss, reading the Rust engine (@teacss/engine)",
7
5
  "license": "MIT",
8
- "author": "Billgo <hi@billgo.me>",
9
- "sideEffects": false,
10
6
  "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/teacss/teacss-rs.git",
10
+ "directory": "packages/inspector"
11
+ },
12
+ "homepage": "https://github.com/teacss/teacss-rs/tree/master/packages/inspector#readme",
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
11
15
  "exports": {
12
- ".": "./dist/index.js",
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ },
13
20
  "./package.json": "./package.json"
14
21
  },
15
- "types": "./dist/index.d.ts",
22
+ "imports": {
23
+ "#src/*": "./dist/*"
24
+ },
16
25
  "files": [
17
- "dist",
18
- "README.md"
26
+ "dist/**/*.js",
27
+ "dist/**/*.d.ts",
28
+ "dist/client",
29
+ "README.md",
30
+ "LICENSE"
19
31
  ],
20
- "scripts": {
21
- "build": "bunx rimraf dist && bunx tsdown --config tsdown.config.mts && bunx vite build",
22
- "dev": "tsdown --watch",
23
- "dev:client": "vite"
32
+ "sideEffects": false,
33
+ "engines": {
34
+ "node": ">=26"
24
35
  },
25
36
  "dependencies": {
26
- "@teacss/core": "0.9.3",
27
- "sirv": "^3.0.2"
37
+ "@teacss/engine": "0.10.2",
38
+ "sirv": "^3"
28
39
  },
29
40
  "devDependencies": {
30
- "@teacss/preset-standard": "0.9.3",
41
+ "@types/node": "^22.20.5",
31
42
  "@types/react": "^19.2.18",
32
43
  "@types/react-dom": "^19.2.7",
33
- "@vitejs/devtools-kit": "^0.7.2",
34
44
  "react": "^19.2.8",
35
45
  "react-dom": "^19.2.8",
36
- "vite": "^8.2.2"
37
- },
38
- "peerDependencies": {
39
- "vite": ">=5.0.0"
46
+ "typescript": "^5.9.3",
47
+ "vite": "^7.3.7"
40
48
  },
41
- "engines": {
42
- "node": ">=22.12.0"
49
+ "scripts": {
50
+ "build:client": "vite build",
51
+ "build:ts": "node -e \"for (const d of ['build','dist']) require('fs').rmSync(d,{recursive:true,force:true})\" && tsc -p tsconfig.json && tsc -p tsconfig.test.json",
52
+ "build": "npm run build:ts && npm run build:client",
53
+ "check:types": "tsc -p tsconfig.json && tsc -p tsconfig.test.json --noEmit && tsc -p client/tsconfig.json",
54
+ "test": "npm run build && node --test \"build/tests/**/*.test.js\""
43
55
  },
44
56
  "publishConfig": {
45
- "access": "public"
57
+ "access": "public",
58
+ "registry": "https://registry.npmjs.org/"
46
59
  }
47
60
  }