@bractjs/bractjs 0.1.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.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +586 -0
  3. package/bin/cli.ts +101 -0
  4. package/package.json +58 -0
  5. package/src/__tests__/fixtures/app/root.tsx +9 -0
  6. package/src/__tests__/fixtures/app/routes/_index.tsx +20 -0
  7. package/src/__tests__/integration.test.ts +66 -0
  8. package/src/__tests__/loader.test.ts +89 -0
  9. package/src/__tests__/matcher.test.ts +69 -0
  10. package/src/__tests__/meta.test.ts +81 -0
  11. package/src/__tests__/scanner.test.ts +58 -0
  12. package/src/__tests__/session.test.ts +103 -0
  13. package/src/build/bundler.ts +75 -0
  14. package/src/build/defines.ts +16 -0
  15. package/src/build/directives.ts +67 -0
  16. package/src/build/env-plugin.ts +56 -0
  17. package/src/build/hash.ts +56 -0
  18. package/src/build/manifest.ts +60 -0
  19. package/src/client/ClientRouter.tsx +122 -0
  20. package/src/client/components/Await.tsx +26 -0
  21. package/src/client/components/Form.tsx +67 -0
  22. package/src/client/components/Image.tsx +79 -0
  23. package/src/client/components/Link.tsx +42 -0
  24. package/src/client/components/LiveReload.tsx +16 -0
  25. package/src/client/components/Outlet.tsx +64 -0
  26. package/src/client/components/Scripts.tsx +12 -0
  27. package/src/client/entry.tsx +49 -0
  28. package/src/client/form-utils.ts +12 -0
  29. package/src/client/hooks/useActionData.ts +14 -0
  30. package/src/client/hooks/useFetcher.ts +51 -0
  31. package/src/client/hooks/useLoaderData.ts +14 -0
  32. package/src/client/hooks/useNavigation.ts +12 -0
  33. package/src/client/hooks/useParams.ts +14 -0
  34. package/src/client/nav-utils.ts +35 -0
  35. package/src/client/prefetch.ts +32 -0
  36. package/src/client/route-cache.ts +20 -0
  37. package/src/client/router.tsx +54 -0
  38. package/src/client/types.ts +23 -0
  39. package/src/codegen/route-codegen.ts +99 -0
  40. package/src/dev/error-overlay.ts +33 -0
  41. package/src/dev/hmr-client.ts +43 -0
  42. package/src/dev/hmr-module-handler.ts +47 -0
  43. package/src/dev/hmr-server.ts +51 -0
  44. package/src/dev/rebuilder.ts +95 -0
  45. package/src/dev/server.ts +38 -0
  46. package/src/dev/watcher.ts +32 -0
  47. package/src/image/cache.ts +75 -0
  48. package/src/image/handler.ts +82 -0
  49. package/src/image/optimizer.ts +76 -0
  50. package/src/image/types.ts +27 -0
  51. package/src/index.ts +51 -0
  52. package/src/middleware/authGuard.ts +37 -0
  53. package/src/middleware/cors.ts +36 -0
  54. package/src/middleware/requestLogger.ts +15 -0
  55. package/src/server/action-handler.ts +35 -0
  56. package/src/server/action-registry.ts +41 -0
  57. package/src/server/env.ts +29 -0
  58. package/src/server/index.ts +8 -0
  59. package/src/server/layout.ts +92 -0
  60. package/src/server/loader.ts +80 -0
  61. package/src/server/matcher.ts +99 -0
  62. package/src/server/meta.ts +92 -0
  63. package/src/server/middleware.ts +47 -0
  64. package/src/server/render.ts +65 -0
  65. package/src/server/request-handler.ts +142 -0
  66. package/src/server/response.ts +17 -0
  67. package/src/server/scanner.ts +68 -0
  68. package/src/server/serve.ts +131 -0
  69. package/src/server/session.ts +111 -0
  70. package/src/server/static.ts +40 -0
  71. package/src/shared/context.ts +37 -0
  72. package/src/shared/deferred.ts +55 -0
  73. package/src/shared/error-boundary.tsx +58 -0
  74. package/src/shared/errors.ts +49 -0
  75. package/src/shared/route-types.ts +51 -0
  76. package/templates/new-app/app/root.tsx +20 -0
  77. package/templates/new-app/app/routes/_index.tsx +31 -0
  78. package/templates/new-app/app/routes/about.tsx +21 -0
  79. package/templates/new-app/bractjs.config.ts +14 -0
  80. package/templates/new-app/package.json +20 -0
  81. package/types/config.d.ts +25 -0
  82. package/types/index.d.ts +109 -0
  83. package/types/middleware.d.ts +19 -0
  84. package/types/route.d.ts +41 -0
  85. package/types/session.d.ts +32 -0
@@ -0,0 +1,14 @@
1
+ import type { BractJSConfig } from "bractjs";
2
+
3
+ const config: BractJSConfig = {
4
+ port: 3000,
5
+ appDir: "./app",
6
+ publicDir: "./public",
7
+ buildDir: "./build",
8
+ manifest: { clientEntry: "", routes: {} }, // populated by `bractjs build`
9
+ minify: true,
10
+ sourcemap: "external",
11
+ clientEnv: [],
12
+ };
13
+
14
+ export default config;
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "{{APP_NAME}}",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "bractjs dev",
7
+ "build": "bractjs build",
8
+ "start": "bractjs start"
9
+ },
10
+ "dependencies": {
11
+ "bractjs": "file:{{BRACT_PATH}}",
12
+ "react": "^19",
13
+ "react-dom": "^19"
14
+ },
15
+ "devDependencies": {
16
+ "@types/bun": "latest",
17
+ "@types/react": "^19",
18
+ "@types/react-dom": "^19"
19
+ }
20
+ }
@@ -0,0 +1,25 @@
1
+ export interface BractJSConfig {
2
+ /** TCP port to listen on. Default: 3000. */
3
+ port: number;
4
+ /** Absolute or relative path to the app directory (contains routes/, root.tsx). */
5
+ appDir: string;
6
+ /** Path to the public static assets directory. */
7
+ publicDir: string;
8
+ /** Pre-built server manifest (production). Built by runBuild(). */
9
+ manifest: ServerManifest;
10
+ /** Directory where build output is written. Default: "./build". */
11
+ buildDir?: string;
12
+ /** Source map strategy passed to Bun.build(). Default: "external". */
13
+ sourcemap?: "none" | "linked" | "inline" | "external";
14
+ /** Minify client bundles. Default: true in production. */
15
+ minify?: boolean;
16
+ /** process.env keys allowed to be inlined into client bundles. */
17
+ clientEnv?: string[];
18
+ }
19
+
20
+ export interface ServerManifest {
21
+ /** Hashed path to the main client entry bundle. */
22
+ clientEntry: string;
23
+ /** Map of URL pattern → route asset info. */
24
+ routes: Record<string, { file?: string; chunk?: string }>;
25
+ }
@@ -0,0 +1,109 @@
1
+ import type { ReactNode, Context } from "react";
2
+
3
+ // ── Route types ───────────────────────────────────────────────────────────
4
+ export type {
5
+ LoaderArgs, ActionArgs, MetaDescriptor, MetaArgs,
6
+ LoaderFunction, ActionFunction, MetaFunction, RouteModule,
7
+ } from "./route.d.ts";
8
+
9
+ // ── Config + Server ───────────────────────────────────────────────────────
10
+ export type { BractJSConfig, ServerManifest } from "./config.d.ts";
11
+ import type { MetaDescriptor } from "./route.d.ts";
12
+ import type { BractJSConfig, ServerManifest } from "./config.d.ts";
13
+
14
+ export interface RenderOptions {
15
+ shell: ReactNode;
16
+ loaderData: Record<string, unknown>;
17
+ actionData: unknown;
18
+ params: Record<string, string>;
19
+ pathname: string;
20
+ manifest: ServerManifest;
21
+ meta: MetaDescriptor[];
22
+ status?: number;
23
+ }
24
+
25
+ export declare function createServer(config?: Partial<BractJSConfig>): { stop(): void };
26
+ export declare function renderRoute(options: RenderOptions): Promise<Response>;
27
+ export declare function redirect(url: string, status?: number): Response;
28
+ export declare function json<T>(data: T, init?: ResponseInit): Response;
29
+ export declare function error(message: string, status?: number): Response;
30
+
31
+ // ── Errors ────────────────────────────────────────────────────────────────
32
+ export declare class BractJSError extends Error { readonly status: number; }
33
+ export declare class HttpError extends BractJSError {
34
+ constructor(status: number, message?: string);
35
+ }
36
+ export declare function isRedirect(value: unknown): value is Response;
37
+ export declare function isHttpError(value: unknown): value is HttpError;
38
+ export declare function isBractJSError(value: unknown): value is BractJSError;
39
+
40
+ // ── Deferred ──────────────────────────────────────────────────────────────
41
+ export declare class Deferred<T> { readonly promise: Promise<T>; }
42
+ export declare function defer<T>(data: Record<string, T | Promise<T>>): unknown;
43
+ export declare function isDeferred(value: unknown): boolean;
44
+
45
+ // ── Context ───────────────────────────────────────────────────────────────
46
+ export interface RouteManifest {
47
+ clientEntry: string;
48
+ routes: Record<string, { file?: string; chunk?: string }>;
49
+ }
50
+ export interface BractJSContextValue {
51
+ loaderData: Record<string, unknown>;
52
+ actionData: unknown;
53
+ params: Record<string, string>;
54
+ pathname: string;
55
+ manifest: RouteManifest;
56
+ }
57
+ export declare const BractJSContext: Context<BractJSContextValue>;
58
+ export declare function BractJSProvider(props: { value: BractJSContextValue; children: ReactNode }): ReactNode;
59
+ export declare function useBractJSContext(): BractJSContextValue;
60
+
61
+ // ── Session ───────────────────────────────────────────────────────────────
62
+ export type { SessionData, Session, CommitOptions, SessionStorage, CookieSessionOptions } from "./session.d.ts";
63
+ import type { SessionStorage, CookieSessionOptions } from "./session.d.ts";
64
+ export declare function createCookieSession(options: CookieSessionOptions): SessionStorage;
65
+
66
+ // ── Middleware ────────────────────────────────────────────────────────────
67
+ export type { MiddlewareContext, MiddlewareFn } from "./middleware.d.ts";
68
+ import type { MiddlewareFn, MiddlewareContext } from "./middleware.d.ts";
69
+
70
+ export declare class MiddlewarePipeline {
71
+ use(fn: MiddlewareFn): this;
72
+ run(ctx: MiddlewareContext, handler: () => Promise<Response>): Promise<Response>;
73
+ }
74
+ export declare const pipeline: MiddlewarePipeline;
75
+
76
+ export interface CorsOptions { origin: string | string[]; methods?: string[]; }
77
+ export interface SessionStorageLike { getSession(cookie?: string | null): Promise<{ get(key: string): unknown }>; }
78
+ export interface AuthGuardOptions { session: SessionStorageLike; required?: boolean; }
79
+ export declare function requestLogger(): MiddlewareFn;
80
+ export declare function cors(options: CorsOptions): MiddlewareFn;
81
+ export declare function authGuard(options: AuthGuardOptions): MiddlewareFn;
82
+
83
+ // ── Client components ─────────────────────────────────────────────────────
84
+ export declare function Scripts(): null;
85
+ export declare function LiveReload(): ReactNode;
86
+ export declare function Outlet(): ReactNode;
87
+
88
+ export interface LinkProps { to: string; prefetch?: "hover" | "none"; children?: ReactNode; className?: string; [key: string]: unknown; }
89
+ export declare function Link(props: LinkProps): ReactNode;
90
+
91
+ export interface FormProps { method?: "post" | "put" | "delete"; action?: string; children?: ReactNode; [key: string]: unknown; }
92
+ export declare function Form(props: FormProps): ReactNode;
93
+
94
+ export interface AwaitProps<T> { resolve: Promise<T>; fallback: ReactNode; children: (data: T) => ReactNode; }
95
+ export declare function Await<T>(props: AwaitProps<T>): ReactNode;
96
+
97
+ // ── Client hooks ──────────────────────────────────────────────────────────
98
+ export declare function useLoaderData<T = unknown>(): T;
99
+ export declare function useActionData<T = unknown>(): T | null;
100
+ export declare function useParams(): Record<string, string>;
101
+ export type NavigationState = "idle" | "loading" | "submitting";
102
+ export declare function useNavigation(): { state: NavigationState };
103
+ export interface FetcherResult {
104
+ data: unknown;
105
+ state: NavigationState;
106
+ load(path: string): Promise<void>;
107
+ submit(path: string, opts: { method: string; body: FormData | Record<string, string> }): Promise<void>;
108
+ }
109
+ export declare function useFetcher(): FetcherResult;
@@ -0,0 +1,19 @@
1
+ export interface MiddlewareContext {
2
+ request: Request;
3
+ params: Record<string, string>;
4
+ /** Arbitrary values threaded through the middleware chain into loaders. */
5
+ context: Record<string, unknown>;
6
+ }
7
+
8
+ export type MiddlewareFn = (
9
+ ctx: MiddlewareContext,
10
+ next: () => Promise<Response>,
11
+ ) => Promise<Response>;
12
+
13
+ export declare class MiddlewarePipeline {
14
+ use(fn: MiddlewareFn): this;
15
+ run(ctx: MiddlewareContext, handler: () => Promise<Response>): Promise<Response>;
16
+ }
17
+
18
+ /** Module-level singleton pipeline — register app-wide middleware here. */
19
+ export declare const pipeline: MiddlewarePipeline;
@@ -0,0 +1,41 @@
1
+ import type { ComponentType } from "react";
2
+
3
+ export interface LoaderArgs {
4
+ request: Request;
5
+ params: Record<string, string>;
6
+ context: Record<string, unknown>;
7
+ }
8
+
9
+ export interface ActionArgs extends LoaderArgs {
10
+ formData: FormData;
11
+ }
12
+
13
+ export type MetaDescriptor =
14
+ | { title: string }
15
+ | { name: string; content: string }
16
+ | { property: string; content: string }
17
+ | { [key: string]: string };
18
+
19
+ export interface MetaArgs<T = unknown> {
20
+ loaderData: T;
21
+ params: Record<string, string>;
22
+ }
23
+
24
+ export type LoaderFunction<T = unknown> = (
25
+ args: LoaderArgs,
26
+ ) => Promise<T | Response> | T | Response;
27
+
28
+ export type ActionFunction<T = unknown> = (
29
+ args: ActionArgs,
30
+ ) => Promise<T | Response> | T | Response;
31
+
32
+ export type MetaFunction<T = unknown> = (args: MetaArgs<T>) => MetaDescriptor[];
33
+
34
+ export interface RouteModule<TLoader = unknown, TAction = unknown> {
35
+ loader?: LoaderFunction<TLoader>;
36
+ action?: ActionFunction<TAction>;
37
+ meta?: MetaFunction<TLoader>;
38
+ handle?: Record<string, unknown>;
39
+ ErrorBoundary?: ComponentType<{ error: unknown }>;
40
+ default?: ComponentType;
41
+ }
@@ -0,0 +1,32 @@
1
+ export type SessionData = Record<string, unknown>;
2
+
3
+ export interface Session {
4
+ get(key: string): unknown;
5
+ set(key: string, val: unknown): void;
6
+ delete(key: string): void;
7
+ has(key: string): boolean;
8
+ }
9
+
10
+ export interface CommitOptions {
11
+ maxAge?: number;
12
+ }
13
+
14
+ export interface SessionStorage {
15
+ getSession(cookie?: string | null): Promise<Session>;
16
+ commitSession(session: Session, opts?: CommitOptions): Promise<string>;
17
+ }
18
+
19
+ export interface CookieSessionOptions {
20
+ /** Cookie name (e.g. "__session"). */
21
+ name: string;
22
+ /** One or more secrets for HMAC signing. Rotation: add new secret first. */
23
+ secrets: string[];
24
+ /** Max age in seconds. Omit for session cookie. */
25
+ maxAge?: number;
26
+ /** Set the Secure flag. Default: true. */
27
+ secure?: boolean;
28
+ /** SameSite policy. Default: "Lax". */
29
+ sameSite?: "Strict" | "Lax" | "None";
30
+ }
31
+
32
+ export declare function createCookieSession(options: CookieSessionOptions): SessionStorage;