@timber-js/app 0.2.0-alpha.84 → 0.2.0-alpha.85

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 (58) hide show
  1. package/LICENSE +8 -0
  2. package/dist/_chunks/{actions-YHRCboUO.js → actions-DLnUaR65.js} +2 -2
  3. package/dist/_chunks/{actions-YHRCboUO.js.map → actions-DLnUaR65.js.map} +1 -1
  4. package/dist/_chunks/{chunk-DYhsFzuS.js → chunk-BYIpzuS7.js} +7 -1
  5. package/dist/_chunks/{define-cookie-C9pquwOg.js → define-cookie-BowvzoP0.js} +4 -4
  6. package/dist/_chunks/{define-cookie-C9pquwOg.js.map → define-cookie-BowvzoP0.js.map} +1 -1
  7. package/dist/_chunks/{request-context-Dl0hXED3.js → request-context-CK5tZqIP.js} +2 -2
  8. package/dist/_chunks/{request-context-Dl0hXED3.js.map → request-context-CK5tZqIP.js.map} +1 -1
  9. package/dist/client/form.d.ts +4 -1
  10. package/dist/client/form.d.ts.map +1 -1
  11. package/dist/client/index.js +2 -2
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/config-validation.d.ts +51 -0
  14. package/dist/config-validation.d.ts.map +1 -0
  15. package/dist/cookies/index.js +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1168 -51
  18. package/dist/index.js.map +1 -1
  19. package/dist/plugins/dev-404-page.d.ts +56 -0
  20. package/dist/plugins/dev-404-page.d.ts.map +1 -0
  21. package/dist/plugins/dev-error-overlay.d.ts +14 -11
  22. package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
  23. package/dist/plugins/dev-error-page.d.ts +58 -0
  24. package/dist/plugins/dev-error-page.d.ts.map +1 -0
  25. package/dist/plugins/dev-server.d.ts.map +1 -1
  26. package/dist/plugins/dev-terminal-error.d.ts +28 -0
  27. package/dist/plugins/dev-terminal-error.d.ts.map +1 -0
  28. package/dist/plugins/entries.d.ts.map +1 -1
  29. package/dist/plugins/fonts.d.ts +4 -0
  30. package/dist/plugins/fonts.d.ts.map +1 -1
  31. package/dist/plugins/routing.d.ts.map +1 -1
  32. package/dist/routing/convention-lint.d.ts +41 -0
  33. package/dist/routing/convention-lint.d.ts.map +1 -0
  34. package/dist/server/action-client.d.ts +13 -5
  35. package/dist/server/action-client.d.ts.map +1 -1
  36. package/dist/server/fallback-error.d.ts +9 -5
  37. package/dist/server/fallback-error.d.ts.map +1 -1
  38. package/dist/server/index.js +2 -2
  39. package/dist/server/index.js.map +1 -1
  40. package/dist/server/internal.js +2 -2
  41. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  42. package/package.json +6 -7
  43. package/src/cli.ts +0 -0
  44. package/src/client/form.tsx +10 -5
  45. package/src/config-validation.ts +299 -0
  46. package/src/index.ts +17 -0
  47. package/src/plugins/dev-404-page.ts +418 -0
  48. package/src/plugins/dev-error-overlay.ts +165 -54
  49. package/src/plugins/dev-error-page.ts +536 -0
  50. package/src/plugins/dev-server.ts +63 -10
  51. package/src/plugins/dev-terminal-error.ts +217 -0
  52. package/src/plugins/entries.ts +3 -0
  53. package/src/plugins/fonts.ts +3 -2
  54. package/src/plugins/routing.ts +37 -5
  55. package/src/routing/convention-lint.ts +356 -0
  56. package/src/server/action-client.ts +17 -9
  57. package/src/server/fallback-error.ts +39 -88
  58. package/src/server/rsc-entry/index.ts +34 -2
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Dev 404 page — self-contained HTML page for dev-mode route misses.
3
+ *
4
+ * When no route matches and the user has no 404.tsx, this page shows:
5
+ * - The requested path
6
+ * - All registered routes in the app
7
+ * - "Did you mean?" suggestions based on string similarity
8
+ * - Setup instructions for new projects with no routes
9
+ *
10
+ * Dev-only: this module is only imported in dev mode. It is never
11
+ * included in production builds.
12
+ *
13
+ * Design doc: 21-dev-server.md, 07-routing.md
14
+ */
15
+ interface RouteInfo {
16
+ /** URL path pattern (e.g., "/dashboard/[id]") */
17
+ path: string;
18
+ /** Whether this is a page route or API route handler */
19
+ type: 'page' | 'route';
20
+ }
21
+ /** Minimal segment node shape — matches ManifestSegmentNode. */
22
+ interface SegmentNode {
23
+ segmentName: string;
24
+ segmentType: string;
25
+ urlPath: string;
26
+ page?: {
27
+ filePath: string;
28
+ };
29
+ route?: {
30
+ filePath: string;
31
+ };
32
+ children: SegmentNode[];
33
+ slots: Record<string, SegmentNode> | Map<string, SegmentNode>;
34
+ }
35
+ /**
36
+ * Collect all routable paths from the manifest tree.
37
+ *
38
+ * Walks the segment tree and collects paths for segments that have
39
+ * a page or route handler.
40
+ */
41
+ export declare function collectRoutes(root: SegmentNode): RouteInfo[];
42
+ /**
43
+ * Find routes similar to the requested path.
44
+ *
45
+ * Returns up to 3 suggestions, sorted by similarity.
46
+ * Only includes routes with distance ≤ 40% of the longer string.
47
+ */
48
+ export declare function findSimilarRoutes(requestedPath: string, routes: RouteInfo[]): RouteInfo[];
49
+ /**
50
+ * Generate a dev-mode 404 page with route listing and suggestions.
51
+ *
52
+ * Returns an HTML string for a self-contained error page.
53
+ */
54
+ export declare function generateDev404Page(requestedPath: string, routes: RouteInfo[]): string;
55
+ export {};
56
+ //# sourceMappingURL=dev-404-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-404-page.d.ts","sourceRoot":"","sources":["../../src/plugins/dev-404-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,UAAU,SAAS;IACjB,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,gEAAgE;AAChE,UAAU,WAAW;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5B,KAAK,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CAC/D;AAID;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,EAAE,CAI5D;AA4DD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAezF;AAcD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAiFrF"}
@@ -1,14 +1,24 @@
1
1
  /**
2
2
  * Dev error overlay — formats and sends errors to Vite's browser overlay and stderr.
3
3
  *
4
- * Integrates with Vite's built-in error overlay (`server.ssrFixStacktrace` +
5
- * `server.hot.send`) rather than implementing a custom overlay.
4
+ * Integrates with Vite's built-in error overlay (`server.hot.send`) rather
5
+ * than implementing a custom overlay.
6
+ *
7
+ * Stack trace source-mapping uses the correct Vite environment module graph:
8
+ * RSC errors use `server.environments.rsc.moduleGraph`, SSR/other errors use
9
+ * `server.environments.ssr.moduleGraph`. `server.ssrFixStacktrace()` is NOT
10
+ * used because it hardcodes the SSR module graph, which doesn't contain
11
+ * source maps for RSC modules (separate Vite environment with its own
12
+ * module graph). This caused RSC errors to show transpiled line numbers
13
+ * instead of original source positions.
6
14
  *
7
15
  * Design doc: 21-dev-server.md §"Error Overlay"
8
16
  */
9
17
  import type { ViteDevServer } from 'vite';
10
18
  /** The phase of the pipeline where the error occurred. */
11
19
  export type ErrorPhase = 'module-transform' | 'proxy' | 'middleware' | 'access' | 'render' | 'handler';
20
+ /** Labels for terminal output. */
21
+ export declare const PHASE_LABELS: Record<ErrorPhase, string>;
12
22
  export type FrameType = 'app' | 'framework' | 'internal';
13
23
  /**
14
24
  * Classify a stack frame line by origin.
@@ -38,15 +48,8 @@ export declare function parseFirstAppFrame(stack: string, projectRoot: string):
38
48
  * Falls back to 'render' if no specific phase can be determined.
39
49
  */
40
50
  export declare function classifyErrorPhase(error: Error, projectRoot: string): ErrorPhase;
41
- /**
42
- * Format an error for terminal output.
43
- *
44
- * - Red for the error message and phase label
45
- * - Dim for framework-internal frames
46
- * - Normal for application frames
47
- * - Separate section for component stack (if present)
48
- */
49
- export declare function formatTerminalError(error: Error, phase: ErrorPhase, projectRoot: string): string;
51
+ import { formatTerminalError as _formatTerminalError } from './dev-terminal-error.js';
52
+ export declare const formatTerminalError: typeof _formatTerminalError;
50
53
  /**
51
54
  * Component info extracted from the RSC debug channel.
52
55
  * Contains only names, environments, and stack frames — never source code.
@@ -1 +1 @@
1
- {"version":3,"file":"dev-error-overlay.d.ts","sourceRoot":"","sources":["../../src/plugins/dev-error-overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AAI1C,0DAA0D;AAC1D,MAAM,MAAM,UAAU,GAClB,kBAAkB,GAClB,OAAO,GACP,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,SAAS,CAAC;AAcd,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,CAU/E;AAID;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAUnE;AAID,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAqB5F;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CAehF;AAUD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAiChG;AAID;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,qBAAqB,EAAE,GAAG,MAAM,CAiCjF;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,UAAU,EACjB,WAAW,EAAE,MAAM,EACnB,kBAAkB,CAAC,EAAE,qBAAqB,EAAE,GAC3C,IAAI,CAsCN"}
1
+ {"version":3,"file":"dev-error-overlay.d.ts","sourceRoot":"","sources":["../../src/plugins/dev-error-overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,MAAM,CAAC;AAoC1D,0DAA0D;AAC1D,MAAM,MAAM,UAAU,GAClB,kBAAkB,GAClB,OAAO,GACP,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,SAAS,CAAC;AAEd,kCAAkC;AAClC,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAOnD,CAAC;AAIF,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,CAU/E;AAID;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAUnE;AAID,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAqB5F;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,GAAG,UAAU,CAehF;AAOD,OAAO,EAAE,mBAAmB,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACtF,eAAO,MAAM,mBAAmB,6BAAuB,CAAC;AAIxD;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,qBAAqB,EAAE,GAAG,MAAM,CAiCjF;AAkHD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,UAAU,EACjB,WAAW,EAAE,MAAM,EACnB,kBAAkB,CAAC,EAAE,qBAAqB,EAAE,GAC3C,IAAI,CA0CN"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Dev error page — self-contained HTML error page for dev server 500s.
3
+ *
4
+ * Generates a styled, self-contained HTML page when the RSC pipeline fails
5
+ * and the Vite error overlay can't fire (e.g., first page load before HMR
6
+ * WebSocket connects, RSC entry module crash, early pipeline errors).
7
+ *
8
+ * This is NOT a replacement for Vite's error overlay — it's the fallback
9
+ * for when the overlay's transport (WebSocket) isn't available yet.
10
+ *
11
+ * Dev-only: this module is only imported by dev-server.ts (apply: 'serve').
12
+ * It is never included in production builds.
13
+ *
14
+ * Design doc: 21-dev-server.md §"Error Overlay"
15
+ */
16
+ import { type ErrorPhase } from './dev-error-overlay.js';
17
+ /**
18
+ * HMR connection options for the dev error page auto-reload WebSocket.
19
+ * Derived from Vite's resolved server config so the error page can
20
+ * connect to the correct HMR endpoint (TIM-789).
21
+ */
22
+ export interface DevErrorHmrOptions {
23
+ protocol?: string;
24
+ host?: string;
25
+ port?: number;
26
+ path?: string;
27
+ token?: string;
28
+ }
29
+ /**
30
+ * Extract HMR connection options from a Vite resolved config.
31
+ * Used by the dev server to pass HMR config to the error page generator
32
+ * so the auto-reload WebSocket connects to the correct endpoint.
33
+ */
34
+ export declare function extractHmrOptions(config: {
35
+ server?: {
36
+ hmr?: {
37
+ protocol?: string;
38
+ host?: string;
39
+ port?: number;
40
+ path?: string;
41
+ } | boolean;
42
+ };
43
+ webSocketToken?: string;
44
+ }): DevErrorHmrOptions | undefined;
45
+ /**
46
+ * Generate a self-contained HTML error page for dev server 500 responses.
47
+ *
48
+ * The page includes:
49
+ * - Error message and phase label
50
+ * - Source code context around the first app frame (if readable)
51
+ * - Component stack (for React render errors)
52
+ * - Classified stack trace (app frames highlighted, internals collapsed)
53
+ * - Copy button for the full error
54
+ * - Dark/light mode via prefers-color-scheme
55
+ * - Auto-reconnect script that watches for Vite HMR and reloads
56
+ */
57
+ export declare function generateDevErrorPage(error: Error, phase: ErrorPhase, projectRoot: string, hmrOptions?: DevErrorHmrOptions): string;
58
+ //# sourceMappingURL=dev-error-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-error-page.d.ts","sourceRoot":"","sources":["../../src/plugins/dev-error-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAIL,KAAK,UAAU,EAEhB,MAAM,wBAAwB,CAAC;AAuGhC;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAeD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,MAAM,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAA;KAAE,CAAC;IAChG,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,kBAAkB,GAAG,SAAS,CAkBjC;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,UAAU,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,kBAAkB,GAC9B,MAAM,CAoIR"}
@@ -1 +1 @@
1
- {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../src/plugins/dev-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAGlE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAkC1D;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAkE1D"}
1
+ {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../src/plugins/dev-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAGlE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA6C1D;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAkE1D"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Terminal error formatting — boxed, color-coded error output for dev mode.
3
+ *
4
+ * Produces a visually scannable error block with:
5
+ * - Unicode box-drawing border around the error
6
+ * - Phase badge and error message
7
+ * - First app frame highlighted as the primary action item
8
+ * - OSC 8 clickable file:line links (VSCode terminal, iTerm2, etc.)
9
+ * - Internal/framework frames collapsed with a count
10
+ * - Component stack (for React render errors)
11
+ *
12
+ * Dev-only: this module is only imported by dev-error-overlay.ts.
13
+ *
14
+ * Design doc: 21-dev-server.md §"Error Overlay"
15
+ */
16
+ import { type ErrorPhase } from './dev-error-overlay.js';
17
+ /**
18
+ * Format an error for terminal output with a boxed layout.
19
+ *
20
+ * The output is designed to be scannable at a glance:
21
+ * 1. Red box with phase badge and error message
22
+ * 2. First app frame as a clickable link (the primary action item)
23
+ * 3. App frames listed normally
24
+ * 4. Internal/framework frames collapsed with count
25
+ * 5. Component stack (if present)
26
+ */
27
+ export declare function formatTerminalError(error: Error, phase: ErrorPhase, projectRoot: string): string;
28
+ //# sourceMappingURL=dev-terminal-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-terminal-error.d.ts","sourceRoot":"","sources":["../../src/plugins/dev-terminal-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAKL,KAAK,UAAU,EAEhB,MAAM,wBAAwB,CAAC;AA4GhC;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAkEhG"}
@@ -1 +1 @@
1
- {"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../src/plugins/entries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAkH1D;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAwBxF;AAiDD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAkFxD"}
1
+ {"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../src/plugins/entries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAInC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAqH1D;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAwBxF;AAiDD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAkFxD"}
@@ -22,6 +22,10 @@ import type { FontFaceDescriptor } from '../fonts/types.js';
22
22
  * Keyed by a unique font ID derived from family + config.
23
23
  */
24
24
  export type FontRegistry = Map<string, ExtractedFont>;
25
+ /**
26
+ * Generate a unique font ID from family + config hash.
27
+ */
28
+ export declare function generateFontId(family: string, config: GoogleFontConfig): string;
25
29
  /**
26
30
  * Extract static font config from a font function call in source code.
27
31
  *
@@ -1 +1 @@
1
- {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/plugins/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAGlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAYzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AA6B5D;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AA4CtD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAE7E;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAE5F;AAUD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAkB/D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAqB3E;AAwED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE,kBAAkB,EAAE,GAAG,MAAM,CAwB/F;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,EACtB,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC,GACrD,MAAM,CAOR;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKtE;AAqED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAsYtD"}
1
+ {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/plugins/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAGlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAYzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AA6B5D;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAkBtD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAM/E;AAkBD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAE7E;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAE5F;AAUD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAkB/D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAqB3E;AAwED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC,EAAE,kBAAkB,EAAE,GAAG,MAAM,CAwB/F;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,EACtB,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,CAAC,GACrD,MAAM,CAOR;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKtE;AAqED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAsYtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/plugins/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAWlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA2D1D,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAwIxD"}
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/plugins/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAgBlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA2D1D,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAmKxD"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Convention linter — validates common misconfigurations in the route tree.
3
+ *
4
+ * Runs at scan time (build and dev startup). Each check produces a warning
5
+ * with the file path, what's wrong, and what to do about it.
6
+ *
7
+ * These are warnings, not errors — they don't block the build. The goal is
8
+ * to catch issues that would otherwise produce cryptic runtime behavior
9
+ * (silent 404s, empty pages, confusing React errors).
10
+ *
11
+ * Design doc: 07-routing.md, 10-error-handling.md
12
+ */
13
+ import type { RouteTree } from './types.js';
14
+ export interface ConventionWarning {
15
+ /** Warning ID for deduplication and filtering. */
16
+ id: string;
17
+ /** Human-readable single-line summary. */
18
+ summary: string;
19
+ /** Multi-line details with file path and fix suggestion. */
20
+ details: string;
21
+ /** Severity: 'warn' for potential issues, 'error' for definite misconfigurations. */
22
+ level: 'warn' | 'error';
23
+ }
24
+ /**
25
+ * Run all convention lint checks on a route tree.
26
+ *
27
+ * Returns an array of warnings. Empty array means everything looks good.
28
+ */
29
+ export declare function lintConventions(tree: RouteTree, appDir: string): ConventionWarning[];
30
+ /**
31
+ * Check if the app/ directory exists. Called before scanning.
32
+ * Returns a warning if missing, or null if the directory exists.
33
+ */
34
+ export declare function checkAppDirExists(appDir: string): ConventionWarning | null;
35
+ /**
36
+ * Format warnings for terminal output.
37
+ *
38
+ * Groups by severity, uses colors, and includes fix suggestions.
39
+ */
40
+ export declare function formatConventionWarnings(warnings: ConventionWarning[]): string;
41
+ //# sourceMappingURL=convention-lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convention-lint.d.ts","sourceRoot":"","sources":["../../src/routing/convention-lint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAIzD,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,EAAE,EAAE,MAAM,CAAC;IACX,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACzB;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAqBpF;AAwOD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAe1E;AAUD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAgC9E"}
@@ -122,12 +122,12 @@ export interface ActionBuilder<TCtx> {
122
122
  /** Declare the input schema. Validation errors are returned typed. */
123
123
  schema<TInput>(schema: ActionSchema<TInput>): ActionBuilderWithSchema<TCtx, TInput>;
124
124
  /** Define the action body without input validation. */
125
- action<TData>(fn: (ctx: ActionContext<TCtx, undefined>) => Promise<TData>): ActionFn<undefined, TData>;
125
+ action<TData>(fn: (ctx: ActionContext<TCtx, undefined>) => Promise<TData>): ActionFn<TData, undefined>;
126
126
  }
127
127
  /** Builder after .schema() has been called. */
128
128
  export interface ActionBuilderWithSchema<TCtx, TInput> {
129
129
  /** Define the action body with validated input. */
130
- action<TData>(fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>): ActionFn<TInput, TData>;
130
+ action<TData>(fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>): ActionFn<TData, TInput>;
131
131
  }
132
132
  /**
133
133
  * The final action function. Callable three ways:
@@ -149,10 +149,18 @@ export interface ActionBuilderWithSchema<TCtx, TInput> {
149
149
  export type InputHint<T> = T extends Record<string, unknown> ? {
150
150
  [K in keyof T]: string | undefined;
151
151
  } : T;
152
- export type ActionFn<TInput = unknown, TData = unknown> = {
152
+ /**
153
+ * ActionFn — the callable returned by `createActionClient().action()`.
154
+ *
155
+ * Generic order: `<TData, TInput>` — TData first for backward compatibility.
156
+ * Previously ActionFn had a single `<TData>` generic, so existing code like
157
+ * `ActionFn<MyResult>` must still work with TData in the first position.
158
+ * See TIM-797.
159
+ */
160
+ export type ActionFn<TData = unknown, TInput = unknown> = {
153
161
  /** <form action={fn}> compatibility — React discards the return value. */
154
162
  (formData: FormData): void;
155
- /** Direct call: action(input) — optional when TInput is undefined (no-schema actions). */
163
+ /** Direct call: action(input) — optional when TInput is undefined/unknown (no-schema actions). */
156
164
  (...args: undefined extends TInput ? [input?: TInput] : [input: TInput]): Promise<ActionResult<TData>>;
157
165
  /** React useActionState: action(prevState, formData) */
158
166
  (prevState: ActionResult<TData> | null, formData: FormData): Promise<ActionResult<TData>>;
@@ -204,6 +212,6 @@ export declare function createActionClient<TCtx = Record<string, never>>(config?
204
212
  * )
205
213
  * ```
206
214
  */
207
- export declare function validated<TInput, TData>(schema: ActionSchema<TInput>, handler: (input: TInput) => Promise<TData>): ActionFn<TInput, TData>;
215
+ export declare function validated<TInput, TData>(schema: ActionSchema<TInput>, handler: (input: TInput) => Promise<TData>): ActionFn<TData, TInput>;
208
216
  export {};
209
217
  //# sourceMappingURL=action-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"action-client.d.ts","sourceRoot":"","sources":["../../src/server/action-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"action-client.d.ts","sourceRoot":"","sources":["../../src/server/action-client.ts"],"names":[],"mappings":"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"}
@@ -18,12 +18,16 @@ import type { GlobalErrorFile } from './rsc-entry/error-renderer.js';
18
18
  * In dev: styled HTML with error details.
19
19
  * In prod: renders root error pages via renderErrorPage.
20
20
  */
21
- export declare function renderFallbackError(error: unknown, req: Request, responseHeaders: Headers, isDev: boolean, rootSegment: ManifestSegmentNode, clientBootstrap: ClientBootstrapConfig, globalError?: GlobalErrorFile): Promise<Response>;
21
+ export declare function renderFallbackError(error: unknown, req: Request, responseHeaders: Headers, isDev: boolean, rootSegment: ManifestSegmentNode, clientBootstrap: ClientBootstrapConfig, globalError?: GlobalErrorFile, projectRoot?: string): Promise<Response>;
22
22
  /**
23
- * Render a dev-mode 500 error page with error message and stack trace.
23
+ * Render a dev-mode 500 error page with error details, source context,
24
+ * classified stack trace, and copy button.
24
25
  *
25
- * Returns an HTML Response that displays the error in a styled page.
26
- * The Vite HMR client script is included so the error overlay still fires.
26
+ * Dynamically imports the shared template from `plugins/dev-error-page.ts`
27
+ * so it is NOT pulled into production server bundles. The Vite client script
28
+ * is injected so the error overlay fires when the HMR WebSocket connects.
29
+ *
30
+ * Dev-only — the dynamic import has zero production cost.
27
31
  */
28
- export declare function renderDevErrorPage(error: unknown): Response;
32
+ export declare function renderDevErrorPage(error: unknown, projectRoot?: string): Promise<Response>;
29
33
  //# sourceMappingURL=fallback-error.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fallback-error.d.ts","sourceRoot":"","sources":["../../src/server/fallback-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAIrE;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,OAAO,EACxB,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,mBAAmB,EAChC,eAAe,EAAE,qBAAqB,EACtC,WAAW,CAAC,EAAE,eAAe,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAwCnB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAmF3D"}
1
+ {"version":3,"file":"fallback-error.d.ts","sourceRoot":"","sources":["../../src/server/fallback-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAIrE;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,OAAO,EACxB,KAAK,EAAE,OAAO,EACd,WAAW,EAAE,mBAAmB,EAChC,eAAe,EAAE,qBAAqB,EACtC,WAAW,CAAC,EAAE,eAAe,EAC7B,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,QAAQ,CAAC,CAwCnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyBhG"}
@@ -1,8 +1,8 @@
1
1
  import { n as isDevMode, t as isDebug } from "../_chunks/debug-ECi_61pb.js";
2
2
  import { t as formatSize } from "../_chunks/format-CYBGxKtc.js";
3
3
  import { n as formFlashAls } from "../_chunks/als-registry-HS0LGUl2.js";
4
- import { a as getHeaders, c as getSegmentParams, i as getHeader, n as getCookie, r as getCookies, s as getSearchParams } from "../_chunks/request-context-Dl0hXED3.js";
5
- import { a as revalidateTag, c as RedirectType, d as redirect, f as redirectExternal, i as revalidatePath, o as DenySignal, p as waitUntil, s as RedirectSignal, u as deny } from "../_chunks/actions-YHRCboUO.js";
4
+ import { a as getHeaders, c as getSegmentParams, i as getHeader, n as getCookie, r as getCookies, s as getSearchParams } from "../_chunks/request-context-CK5tZqIP.js";
5
+ import { a as revalidateTag, c as RedirectType, d as redirect, f as redirectExternal, i as revalidatePath, o as DenySignal, p as waitUntil, s as RedirectSignal, u as deny } from "../_chunks/actions-DLnUaR65.js";
6
6
  import { a as getSpanId, d as withSpan, o as getTraceId, t as addSpanEvent } from "../_chunks/tracing-CCYbKn5n.js";
7
7
  //#region src/server/form-data.ts
8
8
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/server/form-data.ts","../../src/server/action-client.ts","../../src/server/form-flash.ts"],"sourcesContent":["/**\n * FormData preprocessing — schema-agnostic conversion of FormData to typed objects.\n *\n * FormData is all strings. Schema validation expects typed values. This module\n * bridges the gap with intelligent coercion that runs *before* schema validation.\n *\n * Inspired by zod-form-data, but schema-agnostic — works with any Standard Schema\n * library (Zod, Valibot, ArkType).\n *\n * See design/08-forms-and-actions.md §\"parseFormData() and coerce helpers\"\n */\n\n// ─── parseFormData ───────────────────────────────────────────────────────\n\n/**\n * Convert FormData into a plain object with intelligent coercion.\n *\n * Handles:\n * - **Duplicate keys → arrays**: `tags=js&tags=ts` → `{ tags: [\"js\", \"ts\"] }`\n * - **Nested dot-paths**: `user.name=Alice` → `{ user: { name: \"Alice\" } }`\n * - **Empty strings → undefined**: Enables `.optional()` semantics in schemas\n * - **Empty Files → undefined**: File inputs with no selection become `undefined`\n * - **Strips `$ACTION_*` fields**: React's internal hidden fields are excluded\n */\nexport function parseFormData(formData: FormData): Record<string, unknown> {\n const flat: Record<string, unknown> = {};\n\n for (const key of new Set(formData.keys())) {\n // Skip React internal fields\n if (key.startsWith('$ACTION_')) continue;\n\n const values = formData.getAll(key);\n const processed = values.map(normalizeValue);\n\n if (processed.length === 1) {\n flat[key] = processed[0];\n } else {\n // Filter out undefined entries from multi-value fields\n flat[key] = processed.filter((v) => v !== undefined);\n }\n }\n\n // Expand dot-notation paths into nested objects\n return expandDotPaths(flat);\n}\n\n/**\n * Normalize a single FormData entry value.\n * - Empty strings → undefined (enables .optional() semantics)\n * - Empty File objects (no selection) → undefined\n * - Everything else passes through as-is\n */\nfunction normalizeValue(value: FormDataEntryValue): unknown {\n if (typeof value === 'string') {\n return value === '' ? undefined : value;\n }\n\n // File input with no selection: browsers submit a File with name=\"\" and size=0\n if (value instanceof File && value.size === 0 && value.name === '') {\n return undefined;\n }\n\n return value;\n}\n\n/**\n * Expand dot-notation keys into nested objects.\n * `{ \"user.name\": \"Alice\", \"user.age\": \"30\" }` → `{ user: { name: \"Alice\", age: \"30\" } }`\n *\n * Keys without dots are left as-is. Bracket notation (e.g. `items[0]`) is NOT\n * supported — use dot notation (`items.0`) instead.\n */\nfunction expandDotPaths(flat: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n let hasDotPaths = false;\n\n // First pass: check if any keys have dots\n for (const key of Object.keys(flat)) {\n if (key.includes('.')) {\n hasDotPaths = true;\n break;\n }\n }\n\n // Fast path: no dot-notation keys, return as-is\n if (!hasDotPaths) return flat;\n\n for (const [key, value] of Object.entries(flat)) {\n if (!key.includes('.')) {\n result[key] = value;\n continue;\n }\n\n const parts = key.split('.');\n let current: Record<string, unknown> = result;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (current[part] === undefined || current[part] === null) {\n current[part] = {};\n }\n // If current[part] is not an object (e.g., a string from a non-dotted key),\n // the dot-path takes precedence\n if (typeof current[part] !== 'object' || current[part] instanceof File) {\n current[part] = {};\n }\n current = current[part] as Record<string, unknown>;\n }\n\n current[parts[parts.length - 1]] = value;\n }\n\n return result;\n}\n\n// ─── Coercion Helpers ────────────────────────────────────────────────────\n\n/**\n * Schema-agnostic coercion primitives for common FormData patterns.\n *\n * These are plain transform functions — they compose with any schema library's\n * `transform`/`preprocess` pipeline:\n *\n * ```ts\n * // Zod\n * z.preprocess(coerce.number, z.number())\n * // Valibot\n * v.pipe(v.unknown(), v.transform(coerce.number), v.number())\n * ```\n */\nexport const coerce = {\n /**\n * Coerce a string to a number.\n * - `\"42\"` → `42`\n * - `\"3.14\"` → `3.14`\n * - `\"\"` / `undefined` / `null` → `undefined`\n * - Non-numeric strings → `undefined` (schema validation will catch this)\n */\n number(value: unknown): number | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n if (typeof value === 'number') return value;\n if (typeof value !== 'string') return undefined;\n const num = Number(value);\n if (Number.isNaN(num)) return undefined;\n return num;\n },\n\n /**\n * Coerce a checkbox value to a boolean.\n * HTML checkboxes submit \"on\" when checked and are absent when unchecked.\n * - `\"on\"` / any truthy string → `true`\n * - `undefined` / `null` / `\"\"` → `false`\n */\n checkbox(value: unknown): boolean {\n if (value === undefined || value === null || value === '') return false;\n if (typeof value === 'boolean') return value;\n // Any non-empty string (typically \"on\") is true\n return typeof value === 'string' && value.length > 0;\n },\n\n /**\n * Parse a JSON string into an object.\n * - Valid JSON string → parsed object\n * - `\"\"` / `undefined` / `null` → `undefined`\n * - Invalid JSON → `undefined` (schema validation will catch this)\n */\n json(value: unknown): unknown {\n if (value === undefined || value === null || value === '') return undefined;\n if (typeof value !== 'string') return value;\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n },\n\n /**\n * Coerce a date string to a Date object.\n * Handles `<input type=\"date\">` (`\"2024-01-15\"`), `<input type=\"datetime-local\">`\n * (`\"2024-01-15T10:30\"`), and full ISO 8601 strings.\n * - Valid date string → `Date`\n * - `\"\"` / `undefined` / `null` → `undefined`\n * - Invalid date strings → `undefined` (schema validation will catch this)\n * - Impossible dates that `new Date()` silently normalizes (e.g. Feb 31) → `undefined`\n */\n date(value: unknown): Date | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n if (value instanceof Date) return value;\n if (typeof value !== 'string') return undefined;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return undefined;\n\n // Overflow detection: extract Y/M/D from the input string and verify\n // they match the parsed Date components. new Date('2024-02-31') silently\n // normalizes to March 2nd — we reject such inputs.\n const ymdMatch = value.match(/^(\\d{4})-(\\d{2})-(\\d{2})/);\n if (ymdMatch) {\n const inputYear = Number(ymdMatch[1]);\n const inputMonth = Number(ymdMatch[2]);\n const inputDay = Number(ymdMatch[3]);\n\n // Use UTC methods for date-only and Z-suffixed strings to avoid\n // timezone offset shifting the day. For datetime-local (no Z suffix),\n // the Date constructor parses in local time, so use local methods.\n const isUTC = value.length === 10 || value.endsWith('Z');\n const parsedYear = isUTC ? date.getUTCFullYear() : date.getFullYear();\n const parsedMonth = isUTC ? date.getUTCMonth() + 1 : date.getMonth() + 1;\n const parsedDay = isUTC ? date.getUTCDate() : date.getDate();\n\n if (inputYear !== parsedYear || inputMonth !== parsedMonth || inputDay !== parsedDay) {\n return undefined;\n }\n }\n\n return date;\n },\n\n /**\n * Create a File coercion function with optional size and mime type validation.\n * Returns the File if valid, `undefined` otherwise.\n *\n * ```ts\n * // Basic — just checks it's a real File\n * z.preprocess(coerce.file(), z.instanceof(File))\n *\n * // With constraints\n * z.preprocess(\n * coerce.file({ maxSize: 5 * 1024 * 1024, accept: ['image/png', 'image/jpeg'] }),\n * z.instanceof(File)\n * )\n * ```\n */\n file(options?: { maxSize?: number; accept?: string[] }): (value: unknown) => File | undefined {\n return (value: unknown): File | undefined => {\n if (value === undefined || value === null || value === '') return undefined;\n if (!(value instanceof File)) return undefined;\n\n // Empty file input (no selection): browsers submit File with name=\"\" and size=0\n if (value.size === 0 && value.name === '') return undefined;\n\n if (options?.maxSize !== undefined && value.size > options.maxSize) {\n return undefined;\n }\n\n if (options?.accept !== undefined && !options.accept.includes(value.type)) {\n return undefined;\n }\n\n return value;\n };\n },\n};\n","/**\n * createActionClient — typed middleware and schema validation for server actions.\n *\n * Inspired by next-safe-action. Provides a builder API:\n * createActionClient({ middleware }) → .schema(z.object(...)) → .action(fn)\n *\n * The resulting action function satisfies both:\n * 1. Direct call: action(input) → Promise<ActionResult>\n * 2. React useActionState: (prevState, formData) => Promise<ActionResult>\n *\n * See design/08-forms-and-actions.md §\"Middleware for Server Actions\"\n */\n\n// ─── ActionError ─────────────────────────────────────────────────────────\n\n/**\n * Typed error class for server actions. Carries a string code and optional data.\n * When thrown from middleware or the action body, the action short-circuits and\n * the client receives `result.serverError`.\n *\n * In production, unexpected errors (non-ActionError) return `{ code: 'INTERNAL_ERROR' }`\n * with no message. In dev, `data.message` is included.\n */\nexport class ActionError<TCode extends string = string> extends Error {\n readonly code: TCode;\n readonly data: Record<string, unknown> | undefined;\n\n constructor(code: TCode, data?: Record<string, unknown>) {\n super(`ActionError: ${code}`);\n this.name = 'ActionError';\n this.code = code;\n this.data = data;\n }\n}\n\n// ─── Standard Schema ──────────────────────────────────────────────────────\n\n/**\n * Standard Schema v1 interface (subset).\n * Zod ≥3.24, Valibot ≥1.0, and ArkType all implement this.\n * See https://github.com/standard-schema/standard-schema\n *\n * We use permissive types here to accept all compliant libraries without\n * requiring exact structural matches on issues/path shapes.\n */\ninterface StandardSchemaV1<Output = unknown> {\n '~standard': {\n validate(value: unknown): StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;\n };\n}\n\ntype StandardSchemaResult<Output> =\n | { value: Output; issues?: undefined }\n | { value?: undefined; issues: ReadonlyArray<StandardSchemaIssue> };\n\ninterface StandardSchemaIssue {\n message: string;\n path?: ReadonlyArray<PropertyKey | { key: PropertyKey }>;\n}\n\n/** Check if a schema implements the Standard Schema protocol. */\nfunction isStandardSchema(schema: unknown): schema is StandardSchemaV1 {\n return (\n typeof schema === 'object' &&\n schema !== null &&\n '~standard' in schema &&\n typeof (schema as StandardSchemaV1)['~standard'].validate === 'function'\n );\n}\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\n/**\n * Minimal schema interface — compatible with Zod, Valibot, ArkType, etc.\n *\n * Accepts either:\n * - Standard Schema (preferred): any object with `~standard.validate()`\n * - Legacy parse interface: objects with `.parse()` / `.safeParse()`\n *\n * At runtime, Standard Schema is detected via `~standard` property and\n * takes priority over the legacy interface.\n */\nexport type ActionSchema<T = unknown> = StandardSchemaV1<T> | LegacyActionSchema<T>;\n\n/** Legacy schema interface with .parse() / .safeParse(). */\ninterface LegacyActionSchema<T = unknown> {\n 'parse'(data: unknown): T;\n 'safeParse'?(data: unknown): { success: true; data: T } | { success: false; error: SchemaError };\n // Exclude Standard Schema objects from matching this interface\n '~standard'?: never;\n}\n\n/** Schema validation error shape (for legacy .safeParse()/.parse() interface). */\nexport interface SchemaError {\n issues?: Array<{ path?: Array<string | number>; message: string }>;\n flatten?(): { fieldErrors: Record<string, string[]> };\n}\n\n/** Flattened validation errors keyed by field name. */\nexport type ValidationErrors = Record<string, string[]>;\n\n/** Middleware function: returns context to merge into the action body's ctx. */\nexport type ActionMiddleware<TCtx = Record<string, unknown>> = () => Promise<TCtx> | TCtx;\n\n/** The result type returned to the client. */\nexport type ActionResult<TData = unknown> =\n | { data: TData; validationErrors?: never; serverError?: never; submittedValues?: never }\n | {\n data?: never;\n validationErrors: ValidationErrors;\n serverError?: never;\n /** Raw input values on validation failure — for repopulating form fields. */\n submittedValues?: Record<string, unknown>;\n }\n | {\n data?: never;\n validationErrors?: never;\n serverError: { code: string; data?: Record<string, unknown> };\n submittedValues?: never;\n };\n\n/** Context passed to the action body. */\nexport interface ActionContext<TCtx, TInput> {\n ctx: TCtx;\n input: TInput;\n}\n\n// ─── Builder ─────────────────────────────────────────────────────────────\n\ninterface ActionClientConfig<TCtx> {\n middleware?: ActionMiddleware<TCtx> | ActionMiddleware<Record<string, unknown>>[];\n /** Max file size in bytes. Files exceeding this are rejected with validation errors. */\n fileSizeLimit?: number;\n}\n\n/** Intermediate builder returned by createActionClient(). */\nexport interface ActionBuilder<TCtx> {\n /** Declare the input schema. Validation errors are returned typed. */\n schema<TInput>(schema: ActionSchema<TInput>): ActionBuilderWithSchema<TCtx, TInput>;\n /** Define the action body without input validation. */\n action<TData>(\n fn: (ctx: ActionContext<TCtx, undefined>) => Promise<TData>\n ): ActionFn<undefined, TData>;\n}\n\n/** Builder after .schema() has been called. */\nexport interface ActionBuilderWithSchema<TCtx, TInput> {\n /** Define the action body with validated input. */\n action<TData>(fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>): ActionFn<TInput, TData>;\n}\n\n/**\n * The final action function. Callable three ways:\n * - Direct: action(input) → Promise<ActionResult<TData>>\n * - React useActionState: action(prevState, formData) → Promise<ActionResult<TData>>\n * - React <form action={fn}>: action(formData) → void (return value ignored by React)\n *\n * The third overload exists purely for type compatibility with React's\n * `<form action>` prop, which expects `(formData: FormData) => void`.\n * At runtime the function still returns Promise<ActionResult>, but React\n * discards it. This lets validated actions be passed directly to forms\n * without casts.\n */\n/**\n * Map schema output keys to `string | undefined` for form-facing APIs.\n * HTML form values are always strings, and fields can be absent.\n * Gives autocomplete for field names without lying about value types.\n */\nexport type InputHint<T> =\n T extends Record<string, unknown> ? { [K in keyof T]: string | undefined } : T;\n\nexport type ActionFn<TInput = unknown, TData = unknown> = {\n /** <form action={fn}> compatibility — React discards the return value. */\n (formData: FormData): void;\n /** Direct call: action(input) — optional when TInput is undefined (no-schema actions). */\n (\n ...args: undefined extends TInput ? [input?: TInput] : [input: TInput]\n ): Promise<ActionResult<TData>>;\n /** React useActionState: action(prevState, formData) */\n (prevState: ActionResult<TData> | null, formData: FormData): Promise<ActionResult<TData>>;\n};\n\n// ─── Implementation ──────────────────────────────────────────────────────\n\n/**\n * Run middleware array or single function. Returns merged context.\n */\nasync function runActionMiddleware<TCtx>(\n middleware: ActionMiddleware<TCtx> | ActionMiddleware<Record<string, unknown>>[] | undefined\n): Promise<TCtx> {\n if (!middleware) {\n return {} as TCtx;\n }\n\n if (Array.isArray(middleware)) {\n let merged = {} as Record<string, unknown>;\n for (const mw of middleware) {\n const result = await mw();\n merged = { ...merged, ...result };\n }\n return merged as TCtx;\n }\n\n return await middleware();\n}\n\n// Re-export parseFormData for use throughout the framework\nimport { parseFormData } from './form-data.js';\nimport { formatSize } from '../utils/format.js';\nimport { isDebug, isDevMode } from './debug.js';\nimport { RedirectSignal, DenySignal } from './primitives.js';\n\n/**\n * Extract validation errors from a schema error.\n * Supports Zod's flatten() and generic issues array.\n */\nfunction extractValidationErrors(error: SchemaError): ValidationErrors {\n // Zod-style flatten\n if (typeof error.flatten === 'function') {\n return error.flatten().fieldErrors;\n }\n\n // Generic issues array\n if (error.issues) {\n const errors: ValidationErrors = {};\n for (const issue of error.issues) {\n const path = issue.path?.join('.') ?? '_root';\n if (!errors[path]) errors[path] = [];\n errors[path].push(issue.message);\n }\n return errors;\n }\n\n return { _root: ['Validation failed'] };\n}\n\n/**\n * Extract validation errors from Standard Schema issues.\n */\nfunction extractStandardSchemaErrors(issues: ReadonlyArray<StandardSchemaIssue>): ValidationErrors {\n const errors: ValidationErrors = {};\n for (const issue of issues) {\n const path =\n issue.path\n ?.map((p) => {\n // Standard Schema path items can be { key: ... } objects or bare PropertyKey values\n if (typeof p === 'object' && p !== null && 'key' in p) return String(p.key);\n return String(p);\n })\n .join('.') ?? '_root';\n if (!errors[path]) errors[path] = [];\n errors[path].push(issue.message);\n }\n return Object.keys(errors).length > 0 ? errors : { _root: ['Validation failed'] };\n}\n\n/**\n * Wrap unexpected errors into a safe server error result.\n * ActionError → typed result. Other errors → INTERNAL_ERROR (no leak).\n *\n * Exported for use by action-handler.ts to catch errors from raw 'use server'\n * functions that don't use createActionClient.\n */\nexport function handleActionError(error: unknown): ActionResult<never> {\n if (error instanceof ActionError) {\n return {\n serverError: {\n code: error.code,\n ...(error.data ? { data: error.data } : {}),\n },\n };\n }\n\n // In dev, include the message for debugging.\n // Uses isDevMode() — NOT isDebug() — because this data is sent to the\n // browser. TIMBER_DEBUG must never cause error messages to leak to clients.\n // See design/13-security.md principle 4: \"Errors don't leak.\"\n const devMode = isDevMode();\n return {\n serverError: {\n code: 'INTERNAL_ERROR',\n ...(devMode && error instanceof Error ? { data: { message: error.message } } : {}),\n },\n };\n}\n\n/**\n * Create a typed action client with middleware and schema validation.\n *\n * @example\n * ```ts\n * const action = createActionClient({\n * middleware: async () => {\n * const user = await getUser()\n * if (!user) throw new ActionError('UNAUTHORIZED')\n * return { user }\n * },\n * })\n *\n * export const createTodo = action\n * .schema(z.object({ title: z.string().min(1) }))\n * .action(async ({ input, ctx }) => {\n * await db.todos.create({ ...input, userId: ctx.user.id })\n * })\n * ```\n */\nexport function createActionClient<TCtx = Record<string, never>>(\n config: ActionClientConfig<TCtx> = {}\n): ActionBuilder<TCtx> {\n function buildAction<TInput, TData>(\n schema: ActionSchema<TInput> | undefined,\n fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>\n ): ActionFn<TInput, TData> {\n async function actionHandler(...args: unknown[]): Promise<ActionResult<TData>> {\n try {\n // Run middleware\n const ctx = await runActionMiddleware(config.middleware);\n\n // Determine input — either FormData (from useActionState) or direct arg\n let rawInput: unknown;\n if (args.length === 2 && args[1] instanceof FormData) {\n // Called as (prevState, formData) by React useActionState (with-JS path)\n rawInput = schema ? parseFormData(args[1]) : args[1];\n } else if (args.length === 1 && args[0] instanceof FormData) {\n // No-JS path: React's decodeAction binds FormData as the sole argument.\n // The form POSTs without JavaScript, decodeAction resolves the server\n // reference and binds the FormData, then executeAction calls fn() with\n // no additional args — so the bound FormData arrives as args[0].\n rawInput = schema ? parseFormData(args[0]) : args[0];\n } else {\n // Direct call: action(input)\n rawInput = args[0];\n }\n\n // Validate file sizes before schema validation.\n if (config.fileSizeLimit !== undefined && rawInput && typeof rawInput === 'object') {\n const fileSizeErrors = validateFileSizes(\n rawInput as Record<string, unknown>,\n config.fileSizeLimit\n );\n if (fileSizeErrors) {\n const submittedValues = stripFiles(rawInput);\n return { validationErrors: fileSizeErrors, submittedValues };\n }\n }\n\n // Capture submitted values for repopulation on validation failure.\n // Exclude File objects (can't serialize, shouldn't echo back).\n const submittedValues = schema ? stripFiles(rawInput) : undefined;\n\n // Validate with schema if provided\n let input: TInput;\n if (schema) {\n if (isStandardSchema(schema)) {\n // Standard Schema protocol (Zod ≥3.24, Valibot ≥1.0, ArkType)\n const result = schema['~standard'].validate(rawInput);\n if (result instanceof Promise) {\n throw new Error(\n '[timber] createActionClient: schema returned a Promise — only sync schemas are supported.'\n );\n }\n if (result.issues) {\n const validationErrors = extractStandardSchemaErrors(result.issues);\n logValidationFailure(validationErrors);\n return { validationErrors, submittedValues };\n }\n input = result.value;\n } else if (typeof schema.safeParse === 'function') {\n const result = schema.safeParse(rawInput);\n if (!result.success) {\n const validationErrors = extractValidationErrors(result.error);\n logValidationFailure(validationErrors);\n return { validationErrors, submittedValues };\n }\n input = result.data;\n } else {\n try {\n input = schema.parse(rawInput);\n } catch (parseError) {\n const validationErrors = extractValidationErrors(parseError as SchemaError);\n logValidationFailure(validationErrors);\n return { validationErrors, submittedValues };\n }\n }\n } else {\n input = rawInput as TInput;\n }\n\n // Execute the action body\n const data = await fn({ ctx, input });\n return { data };\n } catch (error) {\n // Re-throw redirect/deny signals — these are control flow, not errors.\n // They must propagate to executeAction() which converts them to proper\n // HTTP responses (302 redirect, 4xx deny). Catching them here would\n // wrap them as INTERNAL_ERROR and break redirect()/redirectExternal()/deny().\n if (error instanceof RedirectSignal || error instanceof DenySignal) {\n throw error;\n }\n return handleActionError(error);\n }\n }\n\n return actionHandler as ActionFn<TInput, TData>;\n }\n\n return {\n schema<TInput>(schema: ActionSchema<TInput>) {\n return {\n action<TData>(\n fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>\n ): ActionFn<TInput, TData> {\n return buildAction(schema, fn);\n },\n };\n },\n action<TData>(\n fn: (ctx: ActionContext<TCtx, undefined>) => Promise<TData>\n ): ActionFn<undefined, TData> {\n return buildAction(undefined, fn as (ctx: ActionContext<TCtx, unknown>) => Promise<TData>);\n },\n };\n}\n\n// ─── validated() ────────────────────────────────────────────────────────\n\n/**\n * Convenience wrapper for the common case: validate input, run handler.\n * No middleware needed.\n *\n * @example\n * ```ts\n * 'use server'\n * import { validated } from '@timber-js/app/server'\n * import { z } from 'zod'\n *\n * export const createTodo = validated(\n * z.object({ title: z.string().min(1) }),\n * async (input) => {\n * await db.todos.create(input)\n * }\n * )\n * ```\n */\nexport function validated<TInput, TData>(\n schema: ActionSchema<TInput>,\n handler: (input: TInput) => Promise<TData>\n): ActionFn<TInput, TData> {\n return createActionClient()\n .schema(schema)\n .action(async ({ input }) => handler(input));\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────\n\n/**\n * Log validation failures in dev mode so developers can see what went wrong.\n * In production, validation errors are only returned to the client.\n */\nfunction logValidationFailure(errors: ValidationErrors): void {\n const isDev = isDebug();\n if (!isDev) return;\n\n const fields = Object.entries(errors)\n .map(([field, messages]) => ` ${field}: ${messages.join(', ')}`)\n .join('\\n');\n console.warn(`[timber] action schema validation failed:\\n${fields}`);\n}\n\n/**\n * Validate that all File objects in the input are within the size limit.\n * Returns validation errors keyed by field name, or null if all files are ok.\n */\nfunction validateFileSizes(input: Record<string, unknown>, limit: number): ValidationErrors | null {\n const errors: ValidationErrors = {};\n const limitKb = Math.round(limit / 1024);\n const limitLabel =\n limit >= 1024 * 1024 ? `${Math.round(limit / (1024 * 1024))}MB` : `${limitKb}KB`;\n\n for (const [key, value] of Object.entries(input)) {\n if (value instanceof File && value.size > limit) {\n errors[key] = [\n `File \"${value.name}\" (${formatSize(value.size)}) exceeds the ${limitLabel} limit`,\n ];\n } else if (Array.isArray(value)) {\n const oversized = value.filter((item) => item instanceof File && item.size > limit);\n if (oversized.length > 0) {\n errors[key] = oversized.map(\n (f: File) => `File \"${f.name}\" (${formatSize(f.size)}) exceeds the ${limitLabel} limit`\n );\n }\n }\n }\n\n return Object.keys(errors).length > 0 ? errors : null;\n}\n\n/**\n * Strip File objects from a value, returning a plain object safe for\n * serialization. File objects can't be serialized and shouldn't be echoed back.\n */\nfunction stripFiles(value: unknown): Record<string, unknown> | undefined {\n if (value === null || value === undefined) return undefined;\n if (typeof value !== 'object') return undefined;\n\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n if (v instanceof File) continue;\n if (Array.isArray(v)) {\n result[k] = v.filter((item) => !(item instanceof File));\n } else if (typeof v === 'object' && v !== null && !(v instanceof File)) {\n result[k] = stripFiles(v) ?? {};\n } else {\n result[k] = v;\n }\n }\n return result;\n}\n","/**\n * Form Flash — ALS-based store for no-JS form action results.\n *\n * When a no-JS form action completes, the server re-renders the page with\n * the action result injected via AsyncLocalStorage instead of redirecting\n * (which would discard the result). Server components read the flash and\n * pass it to client form components as the initial `useActionState` value.\n *\n * This follows the Remix/Rails pattern — the form component becomes the\n * single source of truth for both with-JS (React state) and no-JS (flash).\n *\n * The flash data is server-side only — never serialized to cookies or headers.\n *\n * See design/08-forms-and-actions.md §\"No-JS Error Round-Trip\"\n */\n\nimport type { ValidationErrors } from './action-client.js';\nimport { formFlashAls } from './als-registry.js';\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\n/**\n * Flash data injected into the re-render after a no-JS form submission.\n *\n * This is the action result from the server action, stored in ALS so server\n * components can read it and pass it to client form components as the initial\n * state for `useActionState`. This makes the form component a single source\n * of truth for both with-JS and no-JS paths.\n *\n * The shape matches `ActionResult<unknown>` — it's one of:\n * - `{ data: ... }` — success\n * - `{ validationErrors, submittedValues }` — validation failure\n * - `{ serverError }` — server error\n */\nexport interface FormFlashData {\n /** Success data from the action. */\n data?: unknown;\n /** Validation errors keyed by field name. `_root` for form-level errors. */\n validationErrors?: ValidationErrors;\n /** Raw submitted values for repopulating form fields. File objects are excluded. */\n submittedValues?: Record<string, unknown>;\n /** Server error if the action threw an ActionError. */\n serverError?: { code: string; data?: Record<string, unknown> };\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────\n\n/**\n * Read the form flash data for the current request.\n *\n * Returns `null` if no flash data is present (i.e., this is a normal page\n * render, not a re-render after a no-JS form submission).\n *\n * Pass the flash as the initial state to `useActionState` so the form\n * component has a single source of truth for both with-JS and no-JS paths:\n *\n * ```tsx\n * // app/contact/page.tsx (server component)\n * import { getFormFlash } from '@timber-js/app/server'\n *\n * export default function ContactPage() {\n * const flash = getFormFlash()\n * return <ContactForm flash={flash} />\n * }\n *\n * // app/contact/form.tsx (client component)\n * export function ContactForm({ flash }) {\n * const [result, action, isPending] = useActionState(submitContact, flash)\n * // result is the single source of truth — flash seeds it on no-JS\n * }\n * ```\n */\nexport function getFormFlash(): FormFlashData | null {\n return formFlashAls.getStore() ?? null;\n}\n\n// ─── Framework-Internal ──────────────────────────────────────────────────\n\n/**\n * Run a callback with form flash data in scope.\n *\n * Used by the action handler to re-render the page with validation errors\n * available via `getFormFlash()`. Not part of the public API.\n *\n * @internal\n */\nexport function runWithFormFlash<T>(data: FormFlashData, fn: () => T): T {\n return formFlashAls.run(data, fn);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/server/form-data.ts","../../src/server/action-client.ts","../../src/server/form-flash.ts"],"sourcesContent":["/**\n * FormData preprocessing — schema-agnostic conversion of FormData to typed objects.\n *\n * FormData is all strings. Schema validation expects typed values. This module\n * bridges the gap with intelligent coercion that runs *before* schema validation.\n *\n * Inspired by zod-form-data, but schema-agnostic — works with any Standard Schema\n * library (Zod, Valibot, ArkType).\n *\n * See design/08-forms-and-actions.md §\"parseFormData() and coerce helpers\"\n */\n\n// ─── parseFormData ───────────────────────────────────────────────────────\n\n/**\n * Convert FormData into a plain object with intelligent coercion.\n *\n * Handles:\n * - **Duplicate keys → arrays**: `tags=js&tags=ts` → `{ tags: [\"js\", \"ts\"] }`\n * - **Nested dot-paths**: `user.name=Alice` → `{ user: { name: \"Alice\" } }`\n * - **Empty strings → undefined**: Enables `.optional()` semantics in schemas\n * - **Empty Files → undefined**: File inputs with no selection become `undefined`\n * - **Strips `$ACTION_*` fields**: React's internal hidden fields are excluded\n */\nexport function parseFormData(formData: FormData): Record<string, unknown> {\n const flat: Record<string, unknown> = {};\n\n for (const key of new Set(formData.keys())) {\n // Skip React internal fields\n if (key.startsWith('$ACTION_')) continue;\n\n const values = formData.getAll(key);\n const processed = values.map(normalizeValue);\n\n if (processed.length === 1) {\n flat[key] = processed[0];\n } else {\n // Filter out undefined entries from multi-value fields\n flat[key] = processed.filter((v) => v !== undefined);\n }\n }\n\n // Expand dot-notation paths into nested objects\n return expandDotPaths(flat);\n}\n\n/**\n * Normalize a single FormData entry value.\n * - Empty strings → undefined (enables .optional() semantics)\n * - Empty File objects (no selection) → undefined\n * - Everything else passes through as-is\n */\nfunction normalizeValue(value: FormDataEntryValue): unknown {\n if (typeof value === 'string') {\n return value === '' ? undefined : value;\n }\n\n // File input with no selection: browsers submit a File with name=\"\" and size=0\n if (value instanceof File && value.size === 0 && value.name === '') {\n return undefined;\n }\n\n return value;\n}\n\n/**\n * Expand dot-notation keys into nested objects.\n * `{ \"user.name\": \"Alice\", \"user.age\": \"30\" }` → `{ user: { name: \"Alice\", age: \"30\" } }`\n *\n * Keys without dots are left as-is. Bracket notation (e.g. `items[0]`) is NOT\n * supported — use dot notation (`items.0`) instead.\n */\nfunction expandDotPaths(flat: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n let hasDotPaths = false;\n\n // First pass: check if any keys have dots\n for (const key of Object.keys(flat)) {\n if (key.includes('.')) {\n hasDotPaths = true;\n break;\n }\n }\n\n // Fast path: no dot-notation keys, return as-is\n if (!hasDotPaths) return flat;\n\n for (const [key, value] of Object.entries(flat)) {\n if (!key.includes('.')) {\n result[key] = value;\n continue;\n }\n\n const parts = key.split('.');\n let current: Record<string, unknown> = result;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (current[part] === undefined || current[part] === null) {\n current[part] = {};\n }\n // If current[part] is not an object (e.g., a string from a non-dotted key),\n // the dot-path takes precedence\n if (typeof current[part] !== 'object' || current[part] instanceof File) {\n current[part] = {};\n }\n current = current[part] as Record<string, unknown>;\n }\n\n current[parts[parts.length - 1]] = value;\n }\n\n return result;\n}\n\n// ─── Coercion Helpers ────────────────────────────────────────────────────\n\n/**\n * Schema-agnostic coercion primitives for common FormData patterns.\n *\n * These are plain transform functions — they compose with any schema library's\n * `transform`/`preprocess` pipeline:\n *\n * ```ts\n * // Zod\n * z.preprocess(coerce.number, z.number())\n * // Valibot\n * v.pipe(v.unknown(), v.transform(coerce.number), v.number())\n * ```\n */\nexport const coerce = {\n /**\n * Coerce a string to a number.\n * - `\"42\"` → `42`\n * - `\"3.14\"` → `3.14`\n * - `\"\"` / `undefined` / `null` → `undefined`\n * - Non-numeric strings → `undefined` (schema validation will catch this)\n */\n number(value: unknown): number | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n if (typeof value === 'number') return value;\n if (typeof value !== 'string') return undefined;\n const num = Number(value);\n if (Number.isNaN(num)) return undefined;\n return num;\n },\n\n /**\n * Coerce a checkbox value to a boolean.\n * HTML checkboxes submit \"on\" when checked and are absent when unchecked.\n * - `\"on\"` / any truthy string → `true`\n * - `undefined` / `null` / `\"\"` → `false`\n */\n checkbox(value: unknown): boolean {\n if (value === undefined || value === null || value === '') return false;\n if (typeof value === 'boolean') return value;\n // Any non-empty string (typically \"on\") is true\n return typeof value === 'string' && value.length > 0;\n },\n\n /**\n * Parse a JSON string into an object.\n * - Valid JSON string → parsed object\n * - `\"\"` / `undefined` / `null` → `undefined`\n * - Invalid JSON → `undefined` (schema validation will catch this)\n */\n json(value: unknown): unknown {\n if (value === undefined || value === null || value === '') return undefined;\n if (typeof value !== 'string') return value;\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n },\n\n /**\n * Coerce a date string to a Date object.\n * Handles `<input type=\"date\">` (`\"2024-01-15\"`), `<input type=\"datetime-local\">`\n * (`\"2024-01-15T10:30\"`), and full ISO 8601 strings.\n * - Valid date string → `Date`\n * - `\"\"` / `undefined` / `null` → `undefined`\n * - Invalid date strings → `undefined` (schema validation will catch this)\n * - Impossible dates that `new Date()` silently normalizes (e.g. Feb 31) → `undefined`\n */\n date(value: unknown): Date | undefined {\n if (value === undefined || value === null || value === '') return undefined;\n if (value instanceof Date) return value;\n if (typeof value !== 'string') return undefined;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return undefined;\n\n // Overflow detection: extract Y/M/D from the input string and verify\n // they match the parsed Date components. new Date('2024-02-31') silently\n // normalizes to March 2nd — we reject such inputs.\n const ymdMatch = value.match(/^(\\d{4})-(\\d{2})-(\\d{2})/);\n if (ymdMatch) {\n const inputYear = Number(ymdMatch[1]);\n const inputMonth = Number(ymdMatch[2]);\n const inputDay = Number(ymdMatch[3]);\n\n // Use UTC methods for date-only and Z-suffixed strings to avoid\n // timezone offset shifting the day. For datetime-local (no Z suffix),\n // the Date constructor parses in local time, so use local methods.\n const isUTC = value.length === 10 || value.endsWith('Z');\n const parsedYear = isUTC ? date.getUTCFullYear() : date.getFullYear();\n const parsedMonth = isUTC ? date.getUTCMonth() + 1 : date.getMonth() + 1;\n const parsedDay = isUTC ? date.getUTCDate() : date.getDate();\n\n if (inputYear !== parsedYear || inputMonth !== parsedMonth || inputDay !== parsedDay) {\n return undefined;\n }\n }\n\n return date;\n },\n\n /**\n * Create a File coercion function with optional size and mime type validation.\n * Returns the File if valid, `undefined` otherwise.\n *\n * ```ts\n * // Basic — just checks it's a real File\n * z.preprocess(coerce.file(), z.instanceof(File))\n *\n * // With constraints\n * z.preprocess(\n * coerce.file({ maxSize: 5 * 1024 * 1024, accept: ['image/png', 'image/jpeg'] }),\n * z.instanceof(File)\n * )\n * ```\n */\n file(options?: { maxSize?: number; accept?: string[] }): (value: unknown) => File | undefined {\n return (value: unknown): File | undefined => {\n if (value === undefined || value === null || value === '') return undefined;\n if (!(value instanceof File)) return undefined;\n\n // Empty file input (no selection): browsers submit File with name=\"\" and size=0\n if (value.size === 0 && value.name === '') return undefined;\n\n if (options?.maxSize !== undefined && value.size > options.maxSize) {\n return undefined;\n }\n\n if (options?.accept !== undefined && !options.accept.includes(value.type)) {\n return undefined;\n }\n\n return value;\n };\n },\n};\n","/**\n * createActionClient — typed middleware and schema validation for server actions.\n *\n * Inspired by next-safe-action. Provides a builder API:\n * createActionClient({ middleware }) → .schema(z.object(...)) → .action(fn)\n *\n * The resulting action function satisfies both:\n * 1. Direct call: action(input) → Promise<ActionResult>\n * 2. React useActionState: (prevState, formData) => Promise<ActionResult>\n *\n * See design/08-forms-and-actions.md §\"Middleware for Server Actions\"\n */\n\n// ─── ActionError ─────────────────────────────────────────────────────────\n\n/**\n * Typed error class for server actions. Carries a string code and optional data.\n * When thrown from middleware or the action body, the action short-circuits and\n * the client receives `result.serverError`.\n *\n * In production, unexpected errors (non-ActionError) return `{ code: 'INTERNAL_ERROR' }`\n * with no message. In dev, `data.message` is included.\n */\nexport class ActionError<TCode extends string = string> extends Error {\n readonly code: TCode;\n readonly data: Record<string, unknown> | undefined;\n\n constructor(code: TCode, data?: Record<string, unknown>) {\n super(`ActionError: ${code}`);\n this.name = 'ActionError';\n this.code = code;\n this.data = data;\n }\n}\n\n// ─── Standard Schema ──────────────────────────────────────────────────────\n\n/**\n * Standard Schema v1 interface (subset).\n * Zod ≥3.24, Valibot ≥1.0, and ArkType all implement this.\n * See https://github.com/standard-schema/standard-schema\n *\n * We use permissive types here to accept all compliant libraries without\n * requiring exact structural matches on issues/path shapes.\n */\ninterface StandardSchemaV1<Output = unknown> {\n '~standard': {\n validate(value: unknown): StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;\n };\n}\n\ntype StandardSchemaResult<Output> =\n | { value: Output; issues?: undefined }\n | { value?: undefined; issues: ReadonlyArray<StandardSchemaIssue> };\n\ninterface StandardSchemaIssue {\n message: string;\n path?: ReadonlyArray<PropertyKey | { key: PropertyKey }>;\n}\n\n/** Check if a schema implements the Standard Schema protocol. */\nfunction isStandardSchema(schema: unknown): schema is StandardSchemaV1 {\n return (\n typeof schema === 'object' &&\n schema !== null &&\n '~standard' in schema &&\n typeof (schema as StandardSchemaV1)['~standard'].validate === 'function'\n );\n}\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\n/**\n * Minimal schema interface — compatible with Zod, Valibot, ArkType, etc.\n *\n * Accepts either:\n * - Standard Schema (preferred): any object with `~standard.validate()`\n * - Legacy parse interface: objects with `.parse()` / `.safeParse()`\n *\n * At runtime, Standard Schema is detected via `~standard` property and\n * takes priority over the legacy interface.\n */\nexport type ActionSchema<T = unknown> = StandardSchemaV1<T> | LegacyActionSchema<T>;\n\n/** Legacy schema interface with .parse() / .safeParse(). */\ninterface LegacyActionSchema<T = unknown> {\n 'parse'(data: unknown): T;\n 'safeParse'?(data: unknown): { success: true; data: T } | { success: false; error: SchemaError };\n // Exclude Standard Schema objects from matching this interface\n '~standard'?: never;\n}\n\n/** Schema validation error shape (for legacy .safeParse()/.parse() interface). */\nexport interface SchemaError {\n issues?: Array<{ path?: Array<string | number>; message: string }>;\n flatten?(): { fieldErrors: Record<string, string[]> };\n}\n\n/** Flattened validation errors keyed by field name. */\nexport type ValidationErrors = Record<string, string[]>;\n\n/** Middleware function: returns context to merge into the action body's ctx. */\nexport type ActionMiddleware<TCtx = Record<string, unknown>> = () => Promise<TCtx> | TCtx;\n\n/** The result type returned to the client. */\nexport type ActionResult<TData = unknown> =\n | { data: TData; validationErrors?: never; serverError?: never; submittedValues?: never }\n | {\n data?: never;\n validationErrors: ValidationErrors;\n serverError?: never;\n /** Raw input values on validation failure — for repopulating form fields. */\n submittedValues?: Record<string, unknown>;\n }\n | {\n data?: never;\n validationErrors?: never;\n serverError: { code: string; data?: Record<string, unknown> };\n submittedValues?: never;\n };\n\n/** Context passed to the action body. */\nexport interface ActionContext<TCtx, TInput> {\n ctx: TCtx;\n input: TInput;\n}\n\n// ─── Builder ─────────────────────────────────────────────────────────────\n\ninterface ActionClientConfig<TCtx> {\n middleware?: ActionMiddleware<TCtx> | ActionMiddleware<Record<string, unknown>>[];\n /** Max file size in bytes. Files exceeding this are rejected with validation errors. */\n fileSizeLimit?: number;\n}\n\n/** Intermediate builder returned by createActionClient(). */\nexport interface ActionBuilder<TCtx> {\n /** Declare the input schema. Validation errors are returned typed. */\n schema<TInput>(schema: ActionSchema<TInput>): ActionBuilderWithSchema<TCtx, TInput>;\n /** Define the action body without input validation. */\n action<TData>(\n fn: (ctx: ActionContext<TCtx, undefined>) => Promise<TData>\n ): ActionFn<TData, undefined>;\n}\n\n/** Builder after .schema() has been called. */\nexport interface ActionBuilderWithSchema<TCtx, TInput> {\n /** Define the action body with validated input. */\n action<TData>(fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>): ActionFn<TData, TInput>;\n}\n\n/**\n * The final action function. Callable three ways:\n * - Direct: action(input) → Promise<ActionResult<TData>>\n * - React useActionState: action(prevState, formData) → Promise<ActionResult<TData>>\n * - React <form action={fn}>: action(formData) → void (return value ignored by React)\n *\n * The third overload exists purely for type compatibility with React's\n * `<form action>` prop, which expects `(formData: FormData) => void`.\n * At runtime the function still returns Promise<ActionResult>, but React\n * discards it. This lets validated actions be passed directly to forms\n * without casts.\n */\n/**\n * Map schema output keys to `string | undefined` for form-facing APIs.\n * HTML form values are always strings, and fields can be absent.\n * Gives autocomplete for field names without lying about value types.\n */\nexport type InputHint<T> =\n T extends Record<string, unknown> ? { [K in keyof T]: string | undefined } : T;\n\n/**\n * ActionFn — the callable returned by `createActionClient().action()`.\n *\n * Generic order: `<TData, TInput>` — TData first for backward compatibility.\n * Previously ActionFn had a single `<TData>` generic, so existing code like\n * `ActionFn<MyResult>` must still work with TData in the first position.\n * See TIM-797.\n */\nexport type ActionFn<TData = unknown, TInput = unknown> = {\n /** <form action={fn}> compatibility — React discards the return value. */\n (formData: FormData): void;\n /** Direct call: action(input) — optional when TInput is undefined/unknown (no-schema actions). */\n (\n ...args: undefined extends TInput ? [input?: TInput] : [input: TInput]\n ): Promise<ActionResult<TData>>;\n /** React useActionState: action(prevState, formData) */\n (prevState: ActionResult<TData> | null, formData: FormData): Promise<ActionResult<TData>>;\n};\n\n// ─── Implementation ──────────────────────────────────────────────────────\n\n/**\n * Run middleware array or single function. Returns merged context.\n */\nasync function runActionMiddleware<TCtx>(\n middleware: ActionMiddleware<TCtx> | ActionMiddleware<Record<string, unknown>>[] | undefined\n): Promise<TCtx> {\n if (!middleware) {\n return {} as TCtx;\n }\n\n if (Array.isArray(middleware)) {\n let merged = {} as Record<string, unknown>;\n for (const mw of middleware) {\n const result = await mw();\n merged = { ...merged, ...result };\n }\n return merged as TCtx;\n }\n\n return await middleware();\n}\n\n// Re-export parseFormData for use throughout the framework\nimport { parseFormData } from './form-data.js';\nimport { formatSize } from '../utils/format.js';\nimport { isDebug, isDevMode } from './debug.js';\nimport { RedirectSignal, DenySignal } from './primitives.js';\n\n/**\n * Extract validation errors from a schema error.\n * Supports Zod's flatten() and generic issues array.\n */\nfunction extractValidationErrors(error: SchemaError): ValidationErrors {\n // Zod-style flatten\n if (typeof error.flatten === 'function') {\n return error.flatten().fieldErrors;\n }\n\n // Generic issues array\n if (error.issues) {\n const errors: ValidationErrors = {};\n for (const issue of error.issues) {\n const path = issue.path?.join('.') ?? '_root';\n if (!errors[path]) errors[path] = [];\n errors[path].push(issue.message);\n }\n return errors;\n }\n\n return { _root: ['Validation failed'] };\n}\n\n/**\n * Extract validation errors from Standard Schema issues.\n */\nfunction extractStandardSchemaErrors(issues: ReadonlyArray<StandardSchemaIssue>): ValidationErrors {\n const errors: ValidationErrors = {};\n for (const issue of issues) {\n const path =\n issue.path\n ?.map((p) => {\n // Standard Schema path items can be { key: ... } objects or bare PropertyKey values\n if (typeof p === 'object' && p !== null && 'key' in p) return String(p.key);\n return String(p);\n })\n .join('.') ?? '_root';\n if (!errors[path]) errors[path] = [];\n errors[path].push(issue.message);\n }\n return Object.keys(errors).length > 0 ? errors : { _root: ['Validation failed'] };\n}\n\n/**\n * Wrap unexpected errors into a safe server error result.\n * ActionError → typed result. Other errors → INTERNAL_ERROR (no leak).\n *\n * Exported for use by action-handler.ts to catch errors from raw 'use server'\n * functions that don't use createActionClient.\n */\nexport function handleActionError(error: unknown): ActionResult<never> {\n if (error instanceof ActionError) {\n return {\n serverError: {\n code: error.code,\n ...(error.data ? { data: error.data } : {}),\n },\n };\n }\n\n // In dev, include the message for debugging.\n // Uses isDevMode() — NOT isDebug() — because this data is sent to the\n // browser. TIMBER_DEBUG must never cause error messages to leak to clients.\n // See design/13-security.md principle 4: \"Errors don't leak.\"\n const devMode = isDevMode();\n return {\n serverError: {\n code: 'INTERNAL_ERROR',\n ...(devMode && error instanceof Error ? { data: { message: error.message } } : {}),\n },\n };\n}\n\n/**\n * Create a typed action client with middleware and schema validation.\n *\n * @example\n * ```ts\n * const action = createActionClient({\n * middleware: async () => {\n * const user = await getUser()\n * if (!user) throw new ActionError('UNAUTHORIZED')\n * return { user }\n * },\n * })\n *\n * export const createTodo = action\n * .schema(z.object({ title: z.string().min(1) }))\n * .action(async ({ input, ctx }) => {\n * await db.todos.create({ ...input, userId: ctx.user.id })\n * })\n * ```\n */\nexport function createActionClient<TCtx = Record<string, never>>(\n config: ActionClientConfig<TCtx> = {}\n): ActionBuilder<TCtx> {\n function buildAction<TInput, TData>(\n schema: ActionSchema<TInput> | undefined,\n fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>\n ): ActionFn<TData, TInput> {\n async function actionHandler(...args: unknown[]): Promise<ActionResult<TData>> {\n try {\n // Run middleware\n const ctx = await runActionMiddleware(config.middleware);\n\n // Determine input — either FormData (from useActionState) or direct arg\n let rawInput: unknown;\n if (args.length === 2 && args[1] instanceof FormData) {\n // Called as (prevState, formData) by React useActionState (with-JS path)\n rawInput = schema ? parseFormData(args[1]) : args[1];\n } else if (args.length === 1 && args[0] instanceof FormData) {\n // No-JS path: React's decodeAction binds FormData as the sole argument.\n // The form POSTs without JavaScript, decodeAction resolves the server\n // reference and binds the FormData, then executeAction calls fn() with\n // no additional args — so the bound FormData arrives as args[0].\n rawInput = schema ? parseFormData(args[0]) : args[0];\n } else {\n // Direct call: action(input)\n rawInput = args[0];\n }\n\n // Validate file sizes before schema validation.\n if (config.fileSizeLimit !== undefined && rawInput && typeof rawInput === 'object') {\n const fileSizeErrors = validateFileSizes(\n rawInput as Record<string, unknown>,\n config.fileSizeLimit\n );\n if (fileSizeErrors) {\n const submittedValues = stripFiles(rawInput);\n return { validationErrors: fileSizeErrors, submittedValues };\n }\n }\n\n // Capture submitted values for repopulation on validation failure.\n // Exclude File objects (can't serialize, shouldn't echo back).\n const submittedValues = schema ? stripFiles(rawInput) : undefined;\n\n // Validate with schema if provided\n let input: TInput;\n if (schema) {\n if (isStandardSchema(schema)) {\n // Standard Schema protocol (Zod ≥3.24, Valibot ≥1.0, ArkType)\n const result = schema['~standard'].validate(rawInput);\n if (result instanceof Promise) {\n throw new Error(\n '[timber] createActionClient: schema returned a Promise — only sync schemas are supported.'\n );\n }\n if (result.issues) {\n const validationErrors = extractStandardSchemaErrors(result.issues);\n logValidationFailure(validationErrors);\n return { validationErrors, submittedValues };\n }\n input = result.value;\n } else if (typeof schema.safeParse === 'function') {\n const result = schema.safeParse(rawInput);\n if (!result.success) {\n const validationErrors = extractValidationErrors(result.error);\n logValidationFailure(validationErrors);\n return { validationErrors, submittedValues };\n }\n input = result.data;\n } else {\n try {\n input = schema.parse(rawInput);\n } catch (parseError) {\n const validationErrors = extractValidationErrors(parseError as SchemaError);\n logValidationFailure(validationErrors);\n return { validationErrors, submittedValues };\n }\n }\n } else {\n input = rawInput as TInput;\n }\n\n // Execute the action body\n const data = await fn({ ctx, input });\n return { data };\n } catch (error) {\n // Re-throw redirect/deny signals — these are control flow, not errors.\n // They must propagate to executeAction() which converts them to proper\n // HTTP responses (302 redirect, 4xx deny). Catching them here would\n // wrap them as INTERNAL_ERROR and break redirect()/redirectExternal()/deny().\n if (error instanceof RedirectSignal || error instanceof DenySignal) {\n throw error;\n }\n return handleActionError(error);\n }\n }\n\n return actionHandler as ActionFn<TData, TInput>;\n }\n\n return {\n schema<TInput>(schema: ActionSchema<TInput>) {\n return {\n action<TData>(\n fn: (ctx: ActionContext<TCtx, TInput>) => Promise<TData>\n ): ActionFn<TData, TInput> {\n return buildAction(schema, fn);\n },\n };\n },\n action<TData>(\n fn: (ctx: ActionContext<TCtx, undefined>) => Promise<TData>\n ): ActionFn<TData, undefined> {\n return buildAction(undefined, fn as (ctx: ActionContext<TCtx, unknown>) => Promise<TData>);\n },\n };\n}\n\n// ─── validated() ────────────────────────────────────────────────────────\n\n/**\n * Convenience wrapper for the common case: validate input, run handler.\n * No middleware needed.\n *\n * @example\n * ```ts\n * 'use server'\n * import { validated } from '@timber-js/app/server'\n * import { z } from 'zod'\n *\n * export const createTodo = validated(\n * z.object({ title: z.string().min(1) }),\n * async (input) => {\n * await db.todos.create(input)\n * }\n * )\n * ```\n */\nexport function validated<TInput, TData>(\n schema: ActionSchema<TInput>,\n handler: (input: TInput) => Promise<TData>\n): ActionFn<TData, TInput> {\n return createActionClient()\n .schema(schema)\n .action(async ({ input }) => handler(input));\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────\n\n/**\n * Log validation failures in dev mode so developers can see what went wrong.\n * In production, validation errors are only returned to the client.\n */\nfunction logValidationFailure(errors: ValidationErrors): void {\n const isDev = isDebug();\n if (!isDev) return;\n\n const fields = Object.entries(errors)\n .map(([field, messages]) => ` ${field}: ${messages.join(', ')}`)\n .join('\\n');\n console.warn(`[timber] action schema validation failed:\\n${fields}`);\n}\n\n/**\n * Validate that all File objects in the input are within the size limit.\n * Returns validation errors keyed by field name, or null if all files are ok.\n */\nfunction validateFileSizes(input: Record<string, unknown>, limit: number): ValidationErrors | null {\n const errors: ValidationErrors = {};\n const limitKb = Math.round(limit / 1024);\n const limitLabel =\n limit >= 1024 * 1024 ? `${Math.round(limit / (1024 * 1024))}MB` : `${limitKb}KB`;\n\n for (const [key, value] of Object.entries(input)) {\n if (value instanceof File && value.size > limit) {\n errors[key] = [\n `File \"${value.name}\" (${formatSize(value.size)}) exceeds the ${limitLabel} limit`,\n ];\n } else if (Array.isArray(value)) {\n const oversized = value.filter((item) => item instanceof File && item.size > limit);\n if (oversized.length > 0) {\n errors[key] = oversized.map(\n (f: File) => `File \"${f.name}\" (${formatSize(f.size)}) exceeds the ${limitLabel} limit`\n );\n }\n }\n }\n\n return Object.keys(errors).length > 0 ? errors : null;\n}\n\n/**\n * Strip File objects from a value, returning a plain object safe for\n * serialization. File objects can't be serialized and shouldn't be echoed back.\n */\nfunction stripFiles(value: unknown): Record<string, unknown> | undefined {\n if (value === null || value === undefined) return undefined;\n if (typeof value !== 'object') return undefined;\n\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n if (v instanceof File) continue;\n if (Array.isArray(v)) {\n result[k] = v.filter((item) => !(item instanceof File));\n } else if (typeof v === 'object' && v !== null && !(v instanceof File)) {\n result[k] = stripFiles(v) ?? {};\n } else {\n result[k] = v;\n }\n }\n return result;\n}\n","/**\n * Form Flash — ALS-based store for no-JS form action results.\n *\n * When a no-JS form action completes, the server re-renders the page with\n * the action result injected via AsyncLocalStorage instead of redirecting\n * (which would discard the result). Server components read the flash and\n * pass it to client form components as the initial `useActionState` value.\n *\n * This follows the Remix/Rails pattern — the form component becomes the\n * single source of truth for both with-JS (React state) and no-JS (flash).\n *\n * The flash data is server-side only — never serialized to cookies or headers.\n *\n * See design/08-forms-and-actions.md §\"No-JS Error Round-Trip\"\n */\n\nimport type { ValidationErrors } from './action-client.js';\nimport { formFlashAls } from './als-registry.js';\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\n/**\n * Flash data injected into the re-render after a no-JS form submission.\n *\n * This is the action result from the server action, stored in ALS so server\n * components can read it and pass it to client form components as the initial\n * state for `useActionState`. This makes the form component a single source\n * of truth for both with-JS and no-JS paths.\n *\n * The shape matches `ActionResult<unknown>` — it's one of:\n * - `{ data: ... }` — success\n * - `{ validationErrors, submittedValues }` — validation failure\n * - `{ serverError }` — server error\n */\nexport interface FormFlashData {\n /** Success data from the action. */\n data?: unknown;\n /** Validation errors keyed by field name. `_root` for form-level errors. */\n validationErrors?: ValidationErrors;\n /** Raw submitted values for repopulating form fields. File objects are excluded. */\n submittedValues?: Record<string, unknown>;\n /** Server error if the action threw an ActionError. */\n serverError?: { code: string; data?: Record<string, unknown> };\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────\n\n/**\n * Read the form flash data for the current request.\n *\n * Returns `null` if no flash data is present (i.e., this is a normal page\n * render, not a re-render after a no-JS form submission).\n *\n * Pass the flash as the initial state to `useActionState` so the form\n * component has a single source of truth for both with-JS and no-JS paths:\n *\n * ```tsx\n * // app/contact/page.tsx (server component)\n * import { getFormFlash } from '@timber-js/app/server'\n *\n * export default function ContactPage() {\n * const flash = getFormFlash()\n * return <ContactForm flash={flash} />\n * }\n *\n * // app/contact/form.tsx (client component)\n * export function ContactForm({ flash }) {\n * const [result, action, isPending] = useActionState(submitContact, flash)\n * // result is the single source of truth — flash seeds it on no-JS\n * }\n * ```\n */\nexport function getFormFlash(): FormFlashData | null {\n return formFlashAls.getStore() ?? null;\n}\n\n// ─── Framework-Internal ──────────────────────────────────────────────────\n\n/**\n * Run a callback with form flash data in scope.\n *\n * Used by the action handler to re-render the page with validation errors\n * available via `getFormFlash()`. Not part of the public API.\n *\n * @internal\n */\nexport function runWithFormFlash<T>(data: FormFlashData, fn: () => T): T {\n return formFlashAls.run(data, fn);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,cAAc,UAA6C;CACzE,MAAM,OAAgC,EAAE;AAExC,MAAK,MAAM,OAAO,IAAI,IAAI,SAAS,MAAM,CAAC,EAAE;AAE1C,MAAI,IAAI,WAAW,WAAW,CAAE;EAGhC,MAAM,YADS,SAAS,OAAO,IAAI,CACV,IAAI,eAAe;AAE5C,MAAI,UAAU,WAAW,EACvB,MAAK,OAAO,UAAU;MAGtB,MAAK,OAAO,UAAU,QAAQ,MAAM,MAAM,KAAA,EAAU;;AAKxD,QAAO,eAAe,KAAK;;;;;;;;AAS7B,SAAS,eAAe,OAAoC;AAC1D,KAAI,OAAO,UAAU,SACnB,QAAO,UAAU,KAAK,KAAA,IAAY;AAIpC,KAAI,iBAAiB,QAAQ,MAAM,SAAS,KAAK,MAAM,SAAS,GAC9D;AAGF,QAAO;;;;;;;;;AAUT,SAAS,eAAe,MAAwD;CAC9E,MAAM,SAAkC,EAAE;CAC1C,IAAI,cAAc;AAGlB,MAAK,MAAM,OAAO,OAAO,KAAK,KAAK,CACjC,KAAI,IAAI,SAAS,IAAI,EAAE;AACrB,gBAAc;AACd;;AAKJ,KAAI,CAAC,YAAa,QAAO;AAEzB,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,EAAE;AAC/C,MAAI,CAAC,IAAI,SAAS,IAAI,EAAE;AACtB,UAAO,OAAO;AACd;;EAGF,MAAM,QAAQ,IAAI,MAAM,IAAI;EAC5B,IAAI,UAAmC;AAEvC,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;GACzC,MAAM,OAAO,MAAM;AACnB,OAAI,QAAQ,UAAU,KAAA,KAAa,QAAQ,UAAU,KACnD,SAAQ,QAAQ,EAAE;AAIpB,OAAI,OAAO,QAAQ,UAAU,YAAY,QAAQ,iBAAiB,KAChE,SAAQ,QAAQ,EAAE;AAEpB,aAAU,QAAQ;;AAGpB,UAAQ,MAAM,MAAM,SAAS,MAAM;;AAGrC,QAAO;;;;;;;;;;;;;;;AAkBT,IAAa,SAAS;CAQpB,OAAO,OAAoC;AACzC,MAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU,GAAI,QAAO,KAAA;AAClE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,SAAU,QAAO,KAAA;EACtC,MAAM,MAAM,OAAO,MAAM;AACzB,MAAI,OAAO,MAAM,IAAI,CAAE,QAAO,KAAA;AAC9B,SAAO;;CAST,SAAS,OAAyB;AAChC,MAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU,GAAI,QAAO;AAClE,MAAI,OAAO,UAAU,UAAW,QAAO;AAEvC,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS;;CASrD,KAAK,OAAyB;AAC5B,MAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU,GAAI,QAAO,KAAA;AAClE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,UAAO,KAAK,MAAM,MAAM;UAClB;AACN;;;CAaJ,KAAK,OAAkC;AACrC,MAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU,GAAI,QAAO,KAAA;AAClE,MAAI,iBAAiB,KAAM,QAAO;AAClC,MAAI,OAAO,UAAU,SAAU,QAAO,KAAA;EACtC,MAAM,OAAO,IAAI,KAAK,MAAM;AAC5B,MAAI,OAAO,MAAM,KAAK,SAAS,CAAC,CAAE,QAAO,KAAA;EAKzC,MAAM,WAAW,MAAM,MAAM,2BAA2B;AACxD,MAAI,UAAU;GACZ,MAAM,YAAY,OAAO,SAAS,GAAG;GACrC,MAAM,aAAa,OAAO,SAAS,GAAG;GACtC,MAAM,WAAW,OAAO,SAAS,GAAG;GAKpC,MAAM,QAAQ,MAAM,WAAW,MAAM,MAAM,SAAS,IAAI;GACxD,MAAM,aAAa,QAAQ,KAAK,gBAAgB,GAAG,KAAK,aAAa;GACrE,MAAM,cAAc,QAAQ,KAAK,aAAa,GAAG,IAAI,KAAK,UAAU,GAAG;GACvE,MAAM,YAAY,QAAQ,KAAK,YAAY,GAAG,KAAK,SAAS;AAE5D,OAAI,cAAc,cAAc,eAAe,eAAe,aAAa,UACzE;;AAIJ,SAAO;;CAkBT,KAAK,SAAyF;AAC5F,UAAQ,UAAqC;AAC3C,OAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU,GAAI,QAAO,KAAA;AAClE,OAAI,EAAE,iBAAiB,MAAO,QAAO,KAAA;AAGrC,OAAI,MAAM,SAAS,KAAK,MAAM,SAAS,GAAI,QAAO,KAAA;AAElD,OAAI,SAAS,YAAY,KAAA,KAAa,MAAM,OAAO,QAAQ,QACzD;AAGF,OAAI,SAAS,WAAW,KAAA,KAAa,CAAC,QAAQ,OAAO,SAAS,MAAM,KAAK,CACvE;AAGF,UAAO;;;CAGZ;;;;;;;;;;;;;;;;;;;;;;;ACpOD,IAAa,cAAb,cAAgE,MAAM;CACpE;CACA;CAEA,YAAY,MAAa,MAAgC;AACvD,QAAM,gBAAgB,OAAO;AAC7B,OAAK,OAAO;AACZ,OAAK,OAAO;AACZ,OAAK,OAAO;;;;AA8BhB,SAAS,iBAAiB,QAA6C;AACrE,QACE,OAAO,WAAW,YAClB,WAAW,QACX,eAAe,UACf,OAAQ,OAA4B,aAAa,aAAa;;;;;AAiIlE,eAAe,oBACb,YACe;AACf,KAAI,CAAC,WACH,QAAO,EAAE;AAGX,KAAI,MAAM,QAAQ,WAAW,EAAE;EAC7B,IAAI,SAAS,EAAE;AACf,OAAK,MAAM,MAAM,YAAY;GAC3B,MAAM,SAAS,MAAM,IAAI;AACzB,YAAS;IAAE,GAAG;IAAQ,GAAG;IAAQ;;AAEnC,SAAO;;AAGT,QAAO,MAAM,YAAY;;;;;;AAa3B,SAAS,wBAAwB,OAAsC;AAErE,KAAI,OAAO,MAAM,YAAY,WAC3B,QAAO,MAAM,SAAS,CAAC;AAIzB,KAAI,MAAM,QAAQ;EAChB,MAAM,SAA2B,EAAE;AACnC,OAAK,MAAM,SAAS,MAAM,QAAQ;GAChC,MAAM,OAAO,MAAM,MAAM,KAAK,IAAI,IAAI;AACtC,OAAI,CAAC,OAAO,MAAO,QAAO,QAAQ,EAAE;AACpC,UAAO,MAAM,KAAK,MAAM,QAAQ;;AAElC,SAAO;;AAGT,QAAO,EAAE,OAAO,CAAC,oBAAoB,EAAE;;;;;AAMzC,SAAS,4BAA4B,QAA8D;CACjG,MAAM,SAA2B,EAAE;AACnC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OACJ,MAAM,MACF,KAAK,MAAM;AAEX,OAAI,OAAO,MAAM,YAAY,MAAM,QAAQ,SAAS,EAAG,QAAO,OAAO,EAAE,IAAI;AAC3E,UAAO,OAAO,EAAE;IAChB,CACD,KAAK,IAAI,IAAI;AAClB,MAAI,CAAC,OAAO,MAAO,QAAO,QAAQ,EAAE;AACpC,SAAO,MAAM,KAAK,MAAM,QAAQ;;AAElC,QAAO,OAAO,KAAK,OAAO,CAAC,SAAS,IAAI,SAAS,EAAE,OAAO,CAAC,oBAAoB,EAAE;;;;;;;;;AAUnF,SAAgB,kBAAkB,OAAqC;AACrE,KAAI,iBAAiB,YACnB,QAAO,EACL,aAAa;EACX,MAAM,MAAM;EACZ,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,MAAM,GAAG,EAAE;EAC3C,EACF;AAQH,QAAO,EACL,aAAa;EACX,MAAM;EACN,GAJY,WAAW,IAIR,iBAAiB,QAAQ,EAAE,MAAM,EAAE,SAAS,MAAM,SAAS,EAAE,GAAG,EAAE;EAClF,EACF;;;;;;;;;;;;;;;;;;;;;;AAuBH,SAAgB,mBACd,SAAmC,EAAE,EAChB;CACrB,SAAS,YACP,QACA,IACyB;EACzB,eAAe,cAAc,GAAG,MAA+C;AAC7E,OAAI;IAEF,MAAM,MAAM,MAAM,oBAAoB,OAAO,WAAW;IAGxD,IAAI;AACJ,QAAI,KAAK,WAAW,KAAK,KAAK,cAAc,SAE1C,YAAW,SAAS,cAAc,KAAK,GAAG,GAAG,KAAK;aACzC,KAAK,WAAW,KAAK,KAAK,cAAc,SAKjD,YAAW,SAAS,cAAc,KAAK,GAAG,GAAG,KAAK;QAGlD,YAAW,KAAK;AAIlB,QAAI,OAAO,kBAAkB,KAAA,KAAa,YAAY,OAAO,aAAa,UAAU;KAClF,MAAM,iBAAiB,kBACrB,UACA,OAAO,cACR;AACD,SAAI,eAEF,QAAO;MAAE,kBAAkB;MAAgB,iBADnB,WAAW,SAAS;MACgB;;IAMhE,MAAM,kBAAkB,SAAS,WAAW,SAAS,GAAG,KAAA;IAGxD,IAAI;AACJ,QAAI,OACF,KAAI,iBAAiB,OAAO,EAAE;KAE5B,MAAM,SAAS,OAAO,aAAa,SAAS,SAAS;AACrD,SAAI,kBAAkB,QACpB,OAAM,IAAI,MACR,4FACD;AAEH,SAAI,OAAO,QAAQ;MACjB,MAAM,mBAAmB,4BAA4B,OAAO,OAAO;AACnE,2BAAqB,iBAAiB;AACtC,aAAO;OAAE;OAAkB;OAAiB;;AAE9C,aAAQ,OAAO;eACN,OAAO,OAAO,cAAc,YAAY;KACjD,MAAM,SAAS,OAAO,UAAU,SAAS;AACzC,SAAI,CAAC,OAAO,SAAS;MACnB,MAAM,mBAAmB,wBAAwB,OAAO,MAAM;AAC9D,2BAAqB,iBAAiB;AACtC,aAAO;OAAE;OAAkB;OAAiB;;AAE9C,aAAQ,OAAO;UAEf,KAAI;AACF,aAAQ,OAAO,MAAM,SAAS;aACvB,YAAY;KACnB,MAAM,mBAAmB,wBAAwB,WAA0B;AAC3E,0BAAqB,iBAAiB;AACtC,YAAO;MAAE;MAAkB;MAAiB;;QAIhD,SAAQ;AAKV,WAAO,EAAE,MADI,MAAM,GAAG;KAAE;KAAK;KAAO,CAAC,EACtB;YACR,OAAO;AAKd,QAAI,iBAAiB,kBAAkB,iBAAiB,WACtD,OAAM;AAER,WAAO,kBAAkB,MAAM;;;AAInC,SAAO;;AAGT,QAAO;EACL,OAAe,QAA8B;AAC3C,UAAO,EACL,OACE,IACyB;AACzB,WAAO,YAAY,QAAQ,GAAG;MAEjC;;EAEH,OACE,IAC4B;AAC5B,UAAO,YAAY,KAAA,GAAW,GAA4D;;EAE7F;;;;;;;;;;;;;;;;;;;;AAuBH,SAAgB,UACd,QACA,SACyB;AACzB,QAAO,oBAAoB,CACxB,OAAO,OAAO,CACd,OAAO,OAAO,EAAE,YAAY,QAAQ,MAAM,CAAC;;;;;;AAShD,SAAS,qBAAqB,QAAgC;AAE5D,KAAI,CADU,SAAS,CACX;CAEZ,MAAM,SAAS,OAAO,QAAQ,OAAO,CAClC,KAAK,CAAC,OAAO,cAAc,KAAK,MAAM,IAAI,SAAS,KAAK,KAAK,GAAG,CAChE,KAAK,KAAK;AACb,SAAQ,KAAK,8CAA8C,SAAS;;;;;;AAOtE,SAAS,kBAAkB,OAAgC,OAAwC;CACjG,MAAM,SAA2B,EAAE;CACnC,MAAM,UAAU,KAAK,MAAM,QAAQ,KAAK;CACxC,MAAM,aACJ,SAAS,OAAO,OAAO,GAAG,KAAK,MAAM,SAAS,OAAO,MAAM,CAAC,MAAM,GAAG,QAAQ;AAE/E,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KAAI,iBAAiB,QAAQ,MAAM,OAAO,MACxC,QAAO,OAAO,CACZ,SAAS,MAAM,KAAK,KAAK,WAAW,MAAM,KAAK,CAAC,gBAAgB,WAAW,QAC5E;UACQ,MAAM,QAAQ,MAAM,EAAE;EAC/B,MAAM,YAAY,MAAM,QAAQ,SAAS,gBAAgB,QAAQ,KAAK,OAAO,MAAM;AACnF,MAAI,UAAU,SAAS,EACrB,QAAO,OAAO,UAAU,KACrB,MAAY,SAAS,EAAE,KAAK,KAAK,WAAW,EAAE,KAAK,CAAC,gBAAgB,WAAW,QACjF;;AAKP,QAAO,OAAO,KAAK,OAAO,CAAC,SAAS,IAAI,SAAS;;;;;;AAOnD,SAAS,WAAW,OAAqD;AACvE,KAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO,KAAA;AAClD,KAAI,OAAO,UAAU,SAAU,QAAO,KAAA;CAEtC,MAAM,SAAkC,EAAE;AAC1C,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAiC,EAAE;AACrE,MAAI,aAAa,KAAM;AACvB,MAAI,MAAM,QAAQ,EAAE,CAClB,QAAO,KAAK,EAAE,QAAQ,SAAS,EAAE,gBAAgB,MAAM;WAC9C,OAAO,MAAM,YAAY,MAAM,QAAQ,EAAE,aAAa,MAC/D,QAAO,KAAK,WAAW,EAAE,IAAI,EAAE;MAE/B,QAAO,KAAK;;AAGhB,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpcT,SAAgB,eAAqC;AACnD,QAAO,aAAa,UAAU,IAAI"}