brustjs 0.1.49-alpha → 0.1.51-alpha
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/package.json +39 -15
- package/runtime/cache-sync.ts +291 -0
- package/runtime/cache.ts +4 -0
- package/runtime/cli/build.ts +7 -0
- package/runtime/cli/dev.ts +7 -0
- package/runtime/cli/native-routes-emit.ts +147 -1
- package/runtime/cli/ssg.ts +94 -23
- package/runtime/config.ts +42 -0
- package/runtime/index.d.ts +63 -0
- package/runtime/index.js +57 -52
- package/runtime/index.ts +114 -9
- package/runtime/islands/page-cache.ts +32 -2
- package/runtime/native/runtime.ts +220 -7
- package/runtime/render/fragment.ts +87 -0
- package/runtime/routes.ts +482 -95
- package/runtime/templates.ts +47 -0
- package/runtime/treaty.ts +24 -1
- package/types/action-error.d.ts +18 -0
- package/types/cache-sync.d.ts +42 -0
- package/types/cache.d.ts +20 -0
- package/types/cli/help.d.ts +28 -0
- package/types/cli/jinja-staleness.d.ts +14 -0
- package/types/cli/native-routes-emit.d.ts +217 -0
- package/types/cli/new.d.ts +30 -0
- package/types/cli/templates.d.ts +39 -0
- package/types/client/index.d.ts +14 -0
- package/types/config.d.ts +42 -0
- package/types/cookies.d.ts +25 -0
- package/types/create.d.ts +1 -0
- package/types/css/build.d.ts +11 -0
- package/types/css/component-build.d.ts +17 -0
- package/types/css/component-loader.d.ts +8 -0
- package/types/css/manifest.d.ts +21 -0
- package/types/css/process-modules.d.ts +31 -0
- package/types/css/route-deps.d.ts +20 -0
- package/types/css/scan-imports.d.ts +13 -0
- package/types/css.d.ts +16 -0
- package/types/define-actions.d.ts +133 -0
- package/types/dev/client.d.ts +8 -0
- package/types/dev/coordinator.d.ts +33 -0
- package/types/dev/inject.d.ts +6 -0
- package/types/dev/jinja-reload.d.ts +7 -0
- package/types/dev/tui.d.ts +35 -0
- package/types/dev/watcher.d.ts +34 -0
- package/types/dev/worker-registry.d.ts +17 -0
- package/types/dev/ws-channel.d.ts +39 -0
- package/types/generator.d.ts +23 -0
- package/types/index.d.ts +222 -0
- package/types/islands/brust-page.d.ts +74 -0
- package/types/islands/build.d.ts +49 -0
- package/types/islands/chunk-id.d.ts +10 -0
- package/types/islands/importmap.d.ts +2 -0
- package/types/islands/island.d.ts +65 -0
- package/types/islands/isr-jsx.d.ts +31 -0
- package/types/islands/native-render.d.ts +89 -0
- package/types/loader-cache.d.ts +18 -0
- package/types/mcp/extractor.d.ts +14 -0
- package/types/mcp/manifest.d.ts +23 -0
- package/types/mcp/schema.d.ts +19 -0
- package/types/mcp/server.d.ts +15 -0
- package/types/md/emit.d.ts +72 -0
- package/types/md/render.d.ts +80 -0
- package/types/md/routes.d.ts +119 -0
- package/types/md/scan.d.ts +34 -0
- package/types/md/slug.d.ts +1 -0
- package/types/native/build.d.ts +30 -0
- package/types/native/index.d.ts +2 -0
- package/types/native/runtime.d.ts +52 -0
- package/types/navigation/active-nav.d.ts +2 -0
- package/types/navigation/index.d.ts +5 -0
- package/types/navigation/navigate.d.ts +14 -0
- package/types/navigation/react.d.ts +15 -0
- package/types/navigation/store.d.ts +44 -0
- package/types/render/fragment.d.ts +20 -0
- package/types/render/inject-action-prefix.d.ts +9 -0
- package/types/render/inject-css-link.d.ts +8 -0
- package/types/render/inject-dev-client.d.ts +6 -0
- package/types/render/inject-generator.d.ts +7 -0
- package/types/render/inject-store.d.ts +9 -0
- package/types/render/stream.d.ts +45 -0
- package/types/request-context.d.ts +16 -0
- package/types/routes.d.ts +506 -0
- package/types/sse/handler.d.ts +22 -0
- package/types/standard-schema.d.ts +31 -0
- package/types/store/define-store.d.ts +31 -0
- package/types/store/index.d.ts +5 -0
- package/types/store/react.d.ts +2 -0
- package/types/store/serialize.d.ts +5 -0
- package/types/store/server-context.d.ts +4 -0
- package/types/store/signal.d.ts +18 -0
- package/types/templates.d.ts +18 -0
- package/types/treaty.d.ts +70 -0
- package/types/ws/handler.d.ts +26 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Per-CSS-file entry. Side-effect imports (.css) have exports:null;
|
|
2
|
+
* CSS Modules (.module.css) have a flat name→hashed-name map. */
|
|
3
|
+
export interface ComponentCssModuleEntry {
|
|
4
|
+
/** Absolute URL path served by Rust (e.g. /_brust/css/components/<sha>.css). */
|
|
5
|
+
chunk: string;
|
|
6
|
+
/** Original class name → hashed class name. null for non-module .css. */
|
|
7
|
+
exports: Record<string, string> | null;
|
|
8
|
+
}
|
|
9
|
+
export interface ComponentCssManifest {
|
|
10
|
+
version: 1;
|
|
11
|
+
/** Absolute filesystem path of the source .css file → entry. */
|
|
12
|
+
modules: Record<string, ComponentCssModuleEntry>;
|
|
13
|
+
/** Route.fullPath → ordered, deduplicated chunk href list. */
|
|
14
|
+
routeChunks: Record<string, string[]>;
|
|
15
|
+
}
|
|
16
|
+
/** Read + validate. Returns null when the file doesn't exist (project has
|
|
17
|
+
* no component CSS — treated as a no-op everywhere). Throws on malformed
|
|
18
|
+
* JSON or version mismatch. */
|
|
19
|
+
export declare function readComponentCssManifest(absolutePath: string): Promise<ComponentCssManifest | null>;
|
|
20
|
+
/** Write to disk. Creates the parent directory if needed. */
|
|
21
|
+
export declare function writeComponentCssManifest(absolutePath: string, manifest: ComponentCssManifest): Promise<void>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface ProcessCssOptions {
|
|
2
|
+
/** Absolute path; Lightning CSS uses this to derive [hash] in cssModules pattern. */
|
|
3
|
+
filename: string;
|
|
4
|
+
/** Source CSS text. */
|
|
5
|
+
source: string;
|
|
6
|
+
/** True iff this file is a .module.css. */
|
|
7
|
+
isModule: boolean;
|
|
8
|
+
/** Optional Tailwind compiler. When set, source is piped through it FIRST
|
|
9
|
+
* so @apply directives resolve before module class rewriting. Null when
|
|
10
|
+
* Tailwind isn't available. */
|
|
11
|
+
tailwindCompile: {
|
|
12
|
+
build(candidates: string[]): string;
|
|
13
|
+
sources: {
|
|
14
|
+
base: string;
|
|
15
|
+
pattern: string;
|
|
16
|
+
negated: boolean;
|
|
17
|
+
}[];
|
|
18
|
+
} | null;
|
|
19
|
+
}
|
|
20
|
+
export interface ProcessCssResult {
|
|
21
|
+
/** Compiled CSS bytes. */
|
|
22
|
+
code: Uint8Array;
|
|
23
|
+
/** null for non-module files; original→hashed name map for .module.css. */
|
|
24
|
+
exports: Record<string, string> | null;
|
|
25
|
+
}
|
|
26
|
+
/** Pipe a CSS file through Tailwind (if available) then Lightning CSS.
|
|
27
|
+
* For .module.css, class names are hashed via Lightning's default
|
|
28
|
+
* pattern `[local]_[hash]` where [hash] is file-derived (~6 chars).
|
|
29
|
+
* Two classes in the same file share the suffix; two files with the same
|
|
30
|
+
* class name get different hashes. */
|
|
31
|
+
export declare function processCssFile(opts: ProcessCssOptions): Promise<ProcessCssResult>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CssDep } from './scan-imports.ts';
|
|
2
|
+
export interface RouteForCss {
|
|
3
|
+
/** Route.fullPath (e.g. '/' or '/blog/{slug}'). */
|
|
4
|
+
fullPath: string;
|
|
5
|
+
/** Absolute source files of the route's component CHAIN (root layout → leaf).
|
|
6
|
+
* computeRouteChunks walks the local import graph from each and unions the
|
|
7
|
+
* CSS deps it finds — so a layout's co-located `.module.css` links to every
|
|
8
|
+
* route under it, and a leaf's links only to that route. */
|
|
9
|
+
componentSources: string[];
|
|
10
|
+
}
|
|
11
|
+
/** Build the route → CSS chunk hrefs map.
|
|
12
|
+
*
|
|
13
|
+
* For each route, BFS the LOCAL import graph rooted at the route's component
|
|
14
|
+
* chain (via {@link scanImports}, same default-import walk islands use) and
|
|
15
|
+
* collect the CSS deps of every reachable file. This means a `.module.css`
|
|
16
|
+
* co-located with the component that imports it is enough — no need to also
|
|
17
|
+
* import it in routes.tsx. Chunks are deduplicated and sorted per route. */
|
|
18
|
+
export declare function computeRouteChunks(routes: RouteForCss[], scan: Map<string, CssDep[]>, modules: Record<string, {
|
|
19
|
+
chunk: string;
|
|
20
|
+
}>): Record<string, string[]>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface CssDep {
|
|
2
|
+
/** Absolute path to the .css or .module.css file. */
|
|
3
|
+
path: string;
|
|
4
|
+
/** True iff the file ends with `.module.css`. */
|
|
5
|
+
isModule: boolean;
|
|
6
|
+
/** Default-import binding name (e.g. `styles` for `import styles from ...`).
|
|
7
|
+
* null for side-effect imports (`import './foo.css'`). */
|
|
8
|
+
importedName: string | null;
|
|
9
|
+
}
|
|
10
|
+
/** Walk `scanRoot` recursively, parse every TS/TSX file with the TypeScript
|
|
11
|
+
* compiler API, return a map of source file → CSS deps. Files outside
|
|
12
|
+
* scanRoot, inside ignored dirs, or test files are skipped. */
|
|
13
|
+
export declare function scanCssImports(scanRoot: string): Promise<Map<string, CssDep[]>>;
|
package/types/css.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Configure the list of stylesheet hrefs that the renderer should
|
|
2
|
+
* inject into the SSR HTML before </head>. Replaces any previous list.
|
|
3
|
+
* Called from brust.run() main and worker branches (both need to know
|
|
4
|
+
* so the per-worker renderer can inject). */
|
|
5
|
+
export declare function configureCssEnabled(hrefs: readonly string[]): void;
|
|
6
|
+
/** Returns the configured hrefs as a defensive copy. */
|
|
7
|
+
export declare function getCssHrefs(): readonly string[];
|
|
8
|
+
/** Set the CSS hrefs to inject for a specific route. Replaces any previous
|
|
9
|
+
* list for that route. Called from brust.run() main after the component
|
|
10
|
+
* manifest loads. Keys are route.fullPath strings (e.g. '/' or '/blog/{slug}'). */
|
|
11
|
+
export declare function configureCssHrefsForRoute(routePath: string, hrefs: readonly string[]): void;
|
|
12
|
+
/** Returns the CSS hrefs for a specific route, or [] when none configured.
|
|
13
|
+
* Defensive copy on the way out. */
|
|
14
|
+
export declare function getCssHrefsForRoute(routePath: string): readonly string[];
|
|
15
|
+
/** @internal — used by the unit test suite to wipe both global and per-route state. */
|
|
16
|
+
export declare function _resetCssForTests(): void;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { BrustRequest, Middleware } from './routes.ts';
|
|
2
|
+
import type { StandardSchemaV1, InferOutput } from './standard-schema.ts';
|
|
3
|
+
declare const RESPOND: unique symbol;
|
|
4
|
+
export interface ActionResponseSentinel {
|
|
5
|
+
readonly [RESPOND]: true;
|
|
6
|
+
status: number;
|
|
7
|
+
body: unknown;
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
export declare function isRespondSentinel(v: unknown): v is ActionResponseSentinel;
|
|
11
|
+
export declare function makeRespond(): (body: unknown, init?: {
|
|
12
|
+
status?: number;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
}) => ActionResponseSentinel;
|
|
15
|
+
export interface ActionContext<Body = unknown, Params = Record<string, string>, Query = Record<string, string>> {
|
|
16
|
+
req: BrustRequest;
|
|
17
|
+
body: Body;
|
|
18
|
+
params: Params;
|
|
19
|
+
query: Query;
|
|
20
|
+
headers: Record<string, string>;
|
|
21
|
+
respond: (body: unknown, init?: {
|
|
22
|
+
status?: number;
|
|
23
|
+
headers?: Record<string, string>;
|
|
24
|
+
}) => ActionResponseSentinel;
|
|
25
|
+
}
|
|
26
|
+
type Handler<B, P, Q, R> = (ctx: ActionContext<B, P, Q>) => R | Promise<R>;
|
|
27
|
+
export interface EndpointOptions {
|
|
28
|
+
body?: StandardSchemaV1;
|
|
29
|
+
query?: StandardSchemaV1;
|
|
30
|
+
middleware?: Middleware[];
|
|
31
|
+
/** Build-time MCP tool description (read by the manifest extractor). */
|
|
32
|
+
description?: string;
|
|
33
|
+
/** Declared domain errors, keyed by code. Each value is a StandardSchema for
|
|
34
|
+
* the error's `data` payload. TYPE-ONLY: flows into the treaty client's typed
|
|
35
|
+
* error union; the runtime ignores it (handlers throw `ActionError`). */
|
|
36
|
+
errors?: Record<string, StandardSchemaV1>;
|
|
37
|
+
}
|
|
38
|
+
export interface EndpointDef {
|
|
39
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
|
|
40
|
+
path: string;
|
|
41
|
+
handler: (ctx: ActionContext) => unknown;
|
|
42
|
+
body?: StandardSchemaV1;
|
|
43
|
+
query?: StandardSchemaV1;
|
|
44
|
+
middleware: Middleware[];
|
|
45
|
+
}
|
|
46
|
+
type ParamKeys<P extends string> = P extends `${string}{${infer K}}${infer Rest}` ? (K extends `*${infer C}` ? C : K) | ParamKeys<Rest> : never;
|
|
47
|
+
type Params<P extends string> = [ParamKeys<P>] extends [never] ? Record<string, string> : {
|
|
48
|
+
[K in ParamKeys<P>]: string;
|
|
49
|
+
};
|
|
50
|
+
type BodyOf<O> = O extends {
|
|
51
|
+
body: infer S;
|
|
52
|
+
} ? S extends StandardSchemaV1 ? InferOutput<S> : unknown : unknown;
|
|
53
|
+
type QueryOf<O> = O extends {
|
|
54
|
+
query: infer S;
|
|
55
|
+
} ? S extends StandardSchemaV1 ? InferOutput<S> : unknown : Record<string, string>;
|
|
56
|
+
/** Discriminated error union derived from `opts.errors`. Each declared code maps
|
|
57
|
+
* to `{ code; message; data }` where `data` is the schema's inferred output. */
|
|
58
|
+
type ErrorOf<O> = O extends {
|
|
59
|
+
errors: infer E;
|
|
60
|
+
} ? {
|
|
61
|
+
[K in keyof E & string]: {
|
|
62
|
+
code: K;
|
|
63
|
+
message: string;
|
|
64
|
+
data: E[K] extends StandardSchemaV1 ? InferOutput<E[K]> : unknown;
|
|
65
|
+
};
|
|
66
|
+
}[keyof E & string] : never;
|
|
67
|
+
export type EndpointEntry = {
|
|
68
|
+
input: unknown;
|
|
69
|
+
output: unknown;
|
|
70
|
+
error: unknown;
|
|
71
|
+
};
|
|
72
|
+
export type EndpointMap = Record<string, Partial<Record<EndpointDef['method'], EndpointEntry>>>;
|
|
73
|
+
export declare function isValidEndpointPath(p: string): boolean;
|
|
74
|
+
export interface ActionsBuilder<Acc extends EndpointMap = {}> {
|
|
75
|
+
endpoints: EndpointDef[];
|
|
76
|
+
use(mw: Middleware): ActionsBuilder<Acc>;
|
|
77
|
+
get<P extends string, O extends EndpointOptions, R>(path: P, handler: Handler<BodyOf<O>, Params<P>, QueryOf<O>, R>, opts?: O): ActionsBuilder<Acc & {
|
|
78
|
+
[K in P]: {
|
|
79
|
+
GET: {
|
|
80
|
+
input: QueryOf<O>;
|
|
81
|
+
output: Awaited<R>;
|
|
82
|
+
error: ErrorOf<O>;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
}>;
|
|
86
|
+
post<P extends string, O extends EndpointOptions, R>(path: P, handler: Handler<BodyOf<O>, Params<P>, QueryOf<O>, R>, opts?: O): ActionsBuilder<Acc & {
|
|
87
|
+
[K in P]: {
|
|
88
|
+
POST: {
|
|
89
|
+
input: BodyOf<O>;
|
|
90
|
+
output: Awaited<R>;
|
|
91
|
+
error: ErrorOf<O>;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
}>;
|
|
95
|
+
put<P extends string, O extends EndpointOptions, R>(path: P, handler: Handler<BodyOf<O>, Params<P>, QueryOf<O>, R>, opts?: O): ActionsBuilder<Acc & {
|
|
96
|
+
[K in P]: {
|
|
97
|
+
PUT: {
|
|
98
|
+
input: BodyOf<O>;
|
|
99
|
+
output: Awaited<R>;
|
|
100
|
+
error: ErrorOf<O>;
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
}>;
|
|
104
|
+
patch<P extends string, O extends EndpointOptions, R>(path: P, handler: Handler<BodyOf<O>, Params<P>, QueryOf<O>, R>, opts?: O): ActionsBuilder<Acc & {
|
|
105
|
+
[K in P]: {
|
|
106
|
+
PATCH: {
|
|
107
|
+
input: BodyOf<O>;
|
|
108
|
+
output: Awaited<R>;
|
|
109
|
+
error: ErrorOf<O>;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
}>;
|
|
113
|
+
delete<P extends string, O extends EndpointOptions, R>(path: P, handler: Handler<BodyOf<O>, Params<P>, QueryOf<O>, R>, opts?: O): ActionsBuilder<Acc & {
|
|
114
|
+
[K in P]: {
|
|
115
|
+
DELETE: {
|
|
116
|
+
input: BodyOf<O>;
|
|
117
|
+
output: Awaited<R>;
|
|
118
|
+
error: ErrorOf<O>;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
}>;
|
|
122
|
+
head<P extends string, O extends EndpointOptions, R>(path: P, handler: Handler<BodyOf<O>, Params<P>, QueryOf<O>, R>, opts?: O): ActionsBuilder<Acc & {
|
|
123
|
+
[K in P]: {
|
|
124
|
+
HEAD: {
|
|
125
|
+
input: QueryOf<O>;
|
|
126
|
+
output: Awaited<R>;
|
|
127
|
+
error: ErrorOf<O>;
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
}>;
|
|
131
|
+
}
|
|
132
|
+
export declare function defineActions(): ActionsBuilder;
|
|
133
|
+
export { RESPOND };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Browser dev client. Inlined into the SSR first chunk via a <script
|
|
2
|
+
* type="module">…</script> wrapper. Connects WS at /_brust/dev, handles
|
|
3
|
+
* reload / css-update / error / ok messages, manages a red overlay.
|
|
4
|
+
*
|
|
5
|
+
* Keep this string short — it ships in every dev-mode SSR response. */
|
|
6
|
+
export declare const DEV_CLIENT_JS: string;
|
|
7
|
+
/** Build the full <script> tag that gets spliced before </head>. */
|
|
8
|
+
export declare function buildDevClientTag(): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DevMessage } from './ws-channel.ts';
|
|
2
|
+
import type { ChangeKind } from './watcher.ts';
|
|
3
|
+
export interface CoordinatorDeps {
|
|
4
|
+
workers: {
|
|
5
|
+
terminateAll(): Promise<void>;
|
|
6
|
+
spawnAll(): Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
buildCss: () => Promise<void>;
|
|
9
|
+
buildIslands: () => Promise<void>;
|
|
10
|
+
/** Recompile native-route `.jinja` templates from source and reload them into
|
|
11
|
+
* the minijinja env, so `native: true` routes pick up .tsx edits on reload. */
|
|
12
|
+
reEmitJinja: () => Promise<void>;
|
|
13
|
+
/** Clear the Rust-side island ISR cache. Called on every render-affecting
|
|
14
|
+
* reload (`ts`/`html`/`islands`) so a `.tsx` edit is reflected immediately —
|
|
15
|
+
* a frozen island render from before the edit must never survive a hot reload.
|
|
16
|
+
* Optional: a build without the island-cache addon export omits it. */
|
|
17
|
+
clearIslandCache?: () => void;
|
|
18
|
+
buildComponentCss?: () => Promise<void>;
|
|
19
|
+
snapshotComponentCss?: () => Promise<import('../css/manifest.ts').ComponentCssManifest | null>;
|
|
20
|
+
broadcast: (msg: DevMessage) => Promise<void> | void;
|
|
21
|
+
tui: {
|
|
22
|
+
appendEvent(line: string): void;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export declare class Coordinator {
|
|
26
|
+
private deps;
|
|
27
|
+
private state;
|
|
28
|
+
constructor(deps: CoordinatorDeps);
|
|
29
|
+
handleChange(ev: {
|
|
30
|
+
paths: string[];
|
|
31
|
+
kind: ChangeKind;
|
|
32
|
+
}): Promise<void>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Set the dev-client snippet (full `<script>…</script>` tag) the renderer
|
|
2
|
+
* should inject before `</head>`. Pass `null` to disable injection (the
|
|
3
|
+
* default in non-dev mode). */
|
|
4
|
+
export declare function configureDevClientSnippet(s: string | null): void;
|
|
5
|
+
/** Returns the configured snippet, or `null` when dev mode is off. */
|
|
6
|
+
export declare function getDevClientSnippet(): string | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Called once by `brust dev` after the initial native-template emit. */
|
|
2
|
+
export declare function registerJinjaReEmit(fn: () => Promise<void>): void;
|
|
3
|
+
/** Called by the dev coordinator on a ts/html/islands hot reload. No-op when no
|
|
4
|
+
* callback is registered (e.g. unit tests, or an app with no native routes). */
|
|
5
|
+
export declare function reEmitJinja(): Promise<void>;
|
|
6
|
+
/** Test helper. */
|
|
7
|
+
export declare function _resetForTests(): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
interface Status {
|
|
2
|
+
port: number;
|
|
3
|
+
workers: number;
|
|
4
|
+
watching: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface TuiOptions {
|
|
7
|
+
isTty: boolean;
|
|
8
|
+
write: (s: string) => void;
|
|
9
|
+
capacity?: number;
|
|
10
|
+
}
|
|
11
|
+
/** Astro-style dev output: a clean one-time banner on ready, then event lines
|
|
12
|
+
* that scroll naturally below it. Deliberately NOT a full-screen TUI — it never
|
|
13
|
+
* hides the cursor or clears the screen, so the terminal is left exactly as it
|
|
14
|
+
* was found when the dev server exits (no more vanished cursor on Ctrl-C). */
|
|
15
|
+
export declare class Tui {
|
|
16
|
+
private opts;
|
|
17
|
+
private events;
|
|
18
|
+
private capacity;
|
|
19
|
+
private bannerShown;
|
|
20
|
+
constructor(opts: TuiOptions);
|
|
21
|
+
eventsForTests(): string[];
|
|
22
|
+
/** Print the ready banner. Idempotent — only the first call emits it. */
|
|
23
|
+
updateStatus(s: Status): void;
|
|
24
|
+
/** Append one event. Scrolls below the banner (TTY) or prints a plain line
|
|
25
|
+
* (non-TTY / CI). The in-memory ring buffer backs `eventsForTests`. */
|
|
26
|
+
appendEvent(line: string): void;
|
|
27
|
+
/** Called on shutdown. There is nothing to restore (we never altered the
|
|
28
|
+
* terminal mode); the reset is belt-and-braces so a half-written colored
|
|
29
|
+
* line can't bleed into the user's prompt. */
|
|
30
|
+
stop(): void;
|
|
31
|
+
/** Colorize an event line in TTY mode: a dim timestamp prefix, with ok/error
|
|
32
|
+
* markers tinted. The coordinator emits ` → ok (Nms)` and ` ✗ <msg>`. */
|
|
33
|
+
private format;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type ChangeKind = 'ts' | 'css' | 'component-css' | 'html' | 'islands' | 'md';
|
|
2
|
+
/** Classify a changed path. Returns null when the path should be ignored.
|
|
3
|
+
* `root` is used to compute the relative path for ignore-segment matching.
|
|
4
|
+
* `hasMdRoutes` gates the `'md'` kind (S4): when the app has no md routes, a
|
|
5
|
+
* project `.md` edit (README.md, notes) must not trigger the full reload path
|
|
6
|
+
* (island rebuild + worker restart) — it classifies as null instead. Defaults
|
|
7
|
+
* to true for backward compatibility with callers that don't thread the flag. */
|
|
8
|
+
export declare function classifyPath(absPath: string, root: string, hasMdRoutes?: boolean): ChangeKind | null;
|
|
9
|
+
interface Coalesce {
|
|
10
|
+
add(path: string): void;
|
|
11
|
+
flush(): void;
|
|
12
|
+
}
|
|
13
|
+
/** Internal — exposed for unit tests. */
|
|
14
|
+
export declare function _testCoalesce(debounceMs: number, flush: (paths: string[]) => void): Coalesce;
|
|
15
|
+
export interface CreateWatcherOptions {
|
|
16
|
+
root: string;
|
|
17
|
+
debounceMs?: number;
|
|
18
|
+
/** Whether the app has md routes — gates `.md` classification (see
|
|
19
|
+
* classifyPath). Defaults to true (back-compat). */
|
|
20
|
+
hasMdRoutes?: boolean;
|
|
21
|
+
onChange: (ev: {
|
|
22
|
+
paths: string[];
|
|
23
|
+
kind: ChangeKind;
|
|
24
|
+
}) => void;
|
|
25
|
+
}
|
|
26
|
+
export interface Watcher {
|
|
27
|
+
close(): void;
|
|
28
|
+
}
|
|
29
|
+
/** Watch `root` recursively. Emits one `onChange` call per debounce window
|
|
30
|
+
* with paths classified by the dominant kind. Mixed-kind windows pick
|
|
31
|
+
* by priority: islands > ts > html > css (islands trigger a full restart
|
|
32
|
+
* that subsumes the others). */
|
|
33
|
+
export declare function createWatcher(opts: CreateWatcherOptions): Watcher;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Called once by brust.serve() in dev mode AFTER it spawns the initial
|
|
2
|
+
* pool. Hands the references to the registry so the coordinator can
|
|
3
|
+
* churn them later. */
|
|
4
|
+
export declare function registerInitialPool(workers: Worker[], entry: string, count: number, baseEnv: Record<string, string>): void;
|
|
5
|
+
/** Terminate every Worker with a 2s per-worker grace. If termination
|
|
6
|
+
* doesn't return in time, abandon the reference and continue. */
|
|
7
|
+
export declare function terminateAll(): Promise<void>;
|
|
8
|
+
/** Spawn `count` fresh Workers using the entry + env captured at
|
|
9
|
+
* registerInitialPool time. Each worker gets BRUST_WORKER_ID=i.
|
|
10
|
+
* Resolves only after every fresh worker reports `brust-worker-ready`
|
|
11
|
+
* via postMessage — so the caller (coordinator) doesn't broadcast
|
|
12
|
+
* `reload` against workers whose message listeners aren't installed
|
|
13
|
+
* yet. Falls back after a 5s grace if a worker never signals. */
|
|
14
|
+
export declare function spawnAll(): Promise<void>;
|
|
15
|
+
/** Test helper. */
|
|
16
|
+
export declare function _workersForTests(): Worker[];
|
|
17
|
+
export declare function _resetForTests(): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Route } from '../routes.ts';
|
|
2
|
+
/** Server-to-client protocol. Client never sends after open. */
|
|
3
|
+
export type DevMessage = {
|
|
4
|
+
type: 'building';
|
|
5
|
+
} | {
|
|
6
|
+
type: 'reload';
|
|
7
|
+
} | {
|
|
8
|
+
type: 'css-update';
|
|
9
|
+
href: string;
|
|
10
|
+
} | {
|
|
11
|
+
type: 'error';
|
|
12
|
+
message: string;
|
|
13
|
+
stack?: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: 'ok';
|
|
16
|
+
};
|
|
17
|
+
export declare function _clientCountForTests(): number;
|
|
18
|
+
export declare function _resetForTests(): void;
|
|
19
|
+
/** Build the synthetic /_brust/dev WS route. brust.run() prepends this
|
|
20
|
+
* to opts.routes in dev mode (both main + worker route arrays). */
|
|
21
|
+
export declare function createDevWsRoute(): Route;
|
|
22
|
+
/** Worker-side: install a message listener that receives `dev-broadcast`
|
|
23
|
+
* envelopes from the main process and forwards them to this worker's
|
|
24
|
+
* local clients. Idempotent. After install, posts `brust-worker-ready`
|
|
25
|
+
* back to the parent so spawnAll() knows the worker is ready to relay
|
|
26
|
+
* broadcasts (avoids a race where `reload` is dispatched before the
|
|
27
|
+
* fresh worker's listener is wired). */
|
|
28
|
+
export declare function installWorkerBroadcastListener(): void;
|
|
29
|
+
/** Main-side: push the message to every `/_brust/dev` client through the napi
|
|
30
|
+
* addon. The dev WS is a Rust-owned control channel (see the `/_brust/dev`
|
|
31
|
+
* branch in crates/brust/src/server.rs), so the frame is delivered straight
|
|
32
|
+
* through each connection's Rust-owned send_tx and SURVIVES a hot reload's
|
|
33
|
+
* worker restart.
|
|
34
|
+
*
|
|
35
|
+
* The previous implementation relayed main→worker→worker-local-clients, which
|
|
36
|
+
* lost the `reload` frame: the coordinator broadcasts it AFTER terminateAll(),
|
|
37
|
+
* by which point the connection's worker-side registration had died with the
|
|
38
|
+
* old worker. */
|
|
39
|
+
export declare function broadcast(msg: DevMessage): Promise<void>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface GeneratorStrings {
|
|
2
|
+
/** Full meta tag, e.g. `<meta name="generator" content="brust 0.1.48-alpha"/>` */
|
|
3
|
+
meta: string;
|
|
4
|
+
/** X-Powered-By value, e.g. `brust/0.1.48-alpha` */
|
|
5
|
+
header: string;
|
|
6
|
+
}
|
|
7
|
+
/** Build the resolved strings. Version comes from the brustjs package.json
|
|
8
|
+
* (readVersion never throws — "unknown" degrades to name-only, never a crash).
|
|
9
|
+
* The version is sanitized to attr/header-safe bytes; semver chars only. */
|
|
10
|
+
export declare function generatorStrings(versionOn: boolean): GeneratorStrings;
|
|
11
|
+
/** Insert the generator meta immediately after the compiler-emitted viewport
|
|
12
|
+
* meta. Anchor missing (non-document template) → no-op, never an error.
|
|
13
|
+
* CALLER CONTRACT: pass fresh compiler output — this function does not check
|
|
14
|
+
* for an existing tag, so calling it twice on the same string duplicates.
|
|
15
|
+
* Every emit path recompiles from source each run, which keeps this safe. */
|
|
16
|
+
export declare function insertGeneratorMeta(jinja: string, metaTag: string): string;
|
|
17
|
+
/** Write the decision artifact into `dir` (a jinja out dir), creating it. */
|
|
18
|
+
export declare function writeGeneratorArtifact(dir: string, strings: GeneratorStrings): void;
|
|
19
|
+
/** Read the artifact; null on missing/malformed (caller decides the fallback). */
|
|
20
|
+
export declare function readGeneratorArtifact(dir: string): GeneratorStrings | null;
|
|
21
|
+
/** Artifact if present, else version-on defaults — the spec's fallback policy
|
|
22
|
+
* (an old dist with no artifact behaves as default = version on). */
|
|
23
|
+
export declare function resolveGenerator(dir: string): GeneratorStrings;
|