@timber-js/app 0.2.0-alpha.97 → 0.2.0-alpha.98

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 (102) hide show
  1. package/dist/_chunks/{metadata-routes-DS3eKNmf.js → metadata-routes-BU684ls2.js} +1 -1
  2. package/dist/_chunks/{metadata-routes-DS3eKNmf.js.map → metadata-routes-BU684ls2.js.map} +1 -1
  3. package/dist/_chunks/segment-classify-BjfuctV2.js +137 -0
  4. package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -0
  5. package/dist/_chunks/{interception-BbqMCVXa.js → walkers-VOXgavMF.js} +61 -85
  6. package/dist/_chunks/walkers-VOXgavMF.js.map +1 -0
  7. package/dist/adapters/nitro.d.ts.map +1 -1
  8. package/dist/adapters/nitro.js +55 -5
  9. package/dist/adapters/nitro.js.map +1 -1
  10. package/dist/client/index.js +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +189 -62
  13. package/dist/index.js.map +1 -1
  14. package/dist/plugins/build-report.d.ts +6 -4
  15. package/dist/plugins/build-report.d.ts.map +1 -1
  16. package/dist/plugins/dev-404-page.d.ts +8 -18
  17. package/dist/plugins/dev-404-page.d.ts.map +1 -1
  18. package/dist/routing/index.d.ts +5 -3
  19. package/dist/routing/index.d.ts.map +1 -1
  20. package/dist/routing/index.js +3 -3
  21. package/dist/routing/scanner.d.ts +1 -10
  22. package/dist/routing/scanner.d.ts.map +1 -1
  23. package/dist/routing/segment-classify.d.ts +37 -8
  24. package/dist/routing/segment-classify.d.ts.map +1 -1
  25. package/dist/routing/types.d.ts +63 -23
  26. package/dist/routing/types.d.ts.map +1 -1
  27. package/dist/routing/walkers.d.ts +51 -0
  28. package/dist/routing/walkers.d.ts.map +1 -0
  29. package/dist/server/action-handler.d.ts.map +1 -1
  30. package/dist/server/dev-holding-server.d.ts +4 -2
  31. package/dist/server/dev-holding-server.d.ts.map +1 -1
  32. package/dist/server/html-injector-core.d.ts +212 -0
  33. package/dist/server/html-injector-core.d.ts.map +1 -0
  34. package/dist/server/html-injectors.d.ts +59 -59
  35. package/dist/server/html-injectors.d.ts.map +1 -1
  36. package/dist/server/internal.js +710 -563
  37. package/dist/server/internal.js.map +1 -1
  38. package/dist/server/node-stream-transforms.d.ts +46 -49
  39. package/dist/server/node-stream-transforms.d.ts.map +1 -1
  40. package/dist/server/pipeline-helpers.d.ts +88 -0
  41. package/dist/server/pipeline-helpers.d.ts.map +1 -0
  42. package/dist/server/pipeline-phases.d.ts +97 -0
  43. package/dist/server/pipeline-phases.d.ts.map +1 -0
  44. package/dist/server/pipeline.d.ts +53 -32
  45. package/dist/server/pipeline.d.ts.map +1 -1
  46. package/dist/server/port-resolution.d.ts +117 -0
  47. package/dist/server/port-resolution.d.ts.map +1 -0
  48. package/dist/server/route-matcher.d.ts +20 -47
  49. package/dist/server/route-matcher.d.ts.map +1 -1
  50. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  51. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +74 -0
  52. package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
  53. package/dist/server/status-code-resolver.d.ts +16 -11
  54. package/dist/server/status-code-resolver.d.ts.map +1 -1
  55. package/dist/server/tree-builder.d.ts.map +1 -1
  56. package/dist/utils/directive-parser.d.ts +0 -45
  57. package/dist/utils/directive-parser.d.ts.map +1 -1
  58. package/package.json +7 -6
  59. package/src/adapters/nitro.ts +55 -5
  60. package/src/cli.ts +0 -0
  61. package/src/index.ts +84 -31
  62. package/src/plugins/build-report.ts +13 -22
  63. package/src/plugins/dev-404-page.ts +15 -41
  64. package/src/plugins/routing.ts +14 -12
  65. package/src/routing/codegen.ts +1 -1
  66. package/src/routing/convention-lint.ts +4 -4
  67. package/src/routing/index.ts +5 -3
  68. package/src/routing/interception.ts +1 -1
  69. package/src/routing/scanner.ts +17 -93
  70. package/src/routing/segment-classify.ts +107 -8
  71. package/src/routing/status-file-lint.ts +3 -3
  72. package/src/routing/types.ts +63 -23
  73. package/src/routing/walkers.ts +90 -0
  74. package/src/server/action-handler.ts +6 -0
  75. package/src/server/deny-renderer.ts +5 -5
  76. package/src/server/dev-holding-server.ts +4 -2
  77. package/src/server/fallback-error.ts +1 -1
  78. package/src/server/html-injector-core.ts +403 -0
  79. package/src/server/html-injectors.ts +158 -297
  80. package/src/server/node-stream-transforms.ts +108 -248
  81. package/src/server/pipeline-helpers.ts +180 -0
  82. package/src/server/pipeline-phases.ts +591 -0
  83. package/src/server/pipeline.ts +76 -539
  84. package/src/server/port-resolution.ts +215 -0
  85. package/src/server/route-element-builder.ts +1 -1
  86. package/src/server/route-matcher.ts +28 -60
  87. package/src/server/rsc-entry/api-handler.ts +2 -2
  88. package/src/server/rsc-entry/error-renderer.ts +1 -1
  89. package/src/server/rsc-entry/index.ts +52 -98
  90. package/src/server/rsc-entry/wrap-action-dispatch.ts +156 -0
  91. package/src/server/sitemap-generator.ts +1 -1
  92. package/src/server/slot-resolver.ts +1 -1
  93. package/src/server/status-code-resolver.ts +112 -128
  94. package/src/server/tree-builder.ts +6 -4
  95. package/src/utils/directive-parser.ts +0 -392
  96. package/LICENSE +0 -8
  97. package/dist/_chunks/interception-BbqMCVXa.js.map +0 -1
  98. package/dist/_chunks/segment-classify-BDNn6EzD.js +0 -65
  99. package/dist/_chunks/segment-classify-BDNn6EzD.js.map +0 -1
  100. package/dist/server/manifest-status-resolver.d.ts +0 -58
  101. package/dist/server/manifest-status-resolver.d.ts.map +0 -1
  102. package/src/server/manifest-status-resolver.ts +0 -215
@@ -1 +0,0 @@
1
- {"version":3,"file":"segment-classify-BDNn6EzD.js","names":[],"sources":["../../src/routing/segment-classify.ts"],"sourcesContent":["/**\n * Shared URL segment classifier.\n *\n * Single-pass character parser that classifies a route segment token\n * (e.g. \"dashboard\", \"[id]\", \"[...slug]\", \"[[...path]]\") into a typed\n * discriminated union. Used by both server-side routing and client-side\n * Link interpolation.\n *\n * NO regex. NO Node.js-only APIs. Safe to import from browser code.\n *\n * Malformed input (unclosed brackets, empty names, etc.) falls through\n * to { kind: 'static' } — the safe default.\n *\n * If you change the bracket syntax, update ONLY this file. Every\n * consumer imports from here.\n *\n * See design/07-routing.md §\"Route Segments\"\n */\n\nexport type UrlSegment =\n | { kind: 'static'; value: string }\n | { kind: 'dynamic'; name: string }\n | { kind: 'catch-all'; name: string }\n | { kind: 'optional-catch-all'; name: string };\n\n/**\n * Classify a URL path segment token.\n *\n * Walks the string left-to-right in one pass:\n * 1. If it doesn't start with '[', it's static.\n * 2. Count opening brackets (1 or 2) to detect optional.\n * 3. Check for '...' to detect catch-all.\n * 4. Read the param name up to the closing bracket.\n * 5. Validate the expected closing sequence (']' or ']]').\n * 6. Reject if there are leftover characters after the close.\n *\n * Any structural violation → static (safe default).\n */\nexport function classifyUrlSegment(token: string): UrlSegment {\n const len = token.length;\n\n // Must start with '[' to be dynamic\n if (len === 0 || token[0] !== '[') {\n return { kind: 'static', value: token };\n }\n\n let i = 1;\n\n // Check for optional: '[[...'\n const optional = token[i] === '[';\n if (optional) i++;\n\n // Check for catch-all: '...'\n const catchAll = i + 2 < len && token[i] === '.' && token[i + 1] === '.' && token[i + 2] === '.';\n if (catchAll) i += 3;\n\n // Read param name — everything up to ']'\n const nameStart = i;\n while (i < len && token[i] !== ']') i++;\n\n // Must have found a ']' and name must be non-empty\n if (i >= len || i === nameStart) {\n return { kind: 'static', value: token };\n }\n\n const name = token.slice(nameStart, i);\n i++; // skip first ']'\n\n // Optional requires a second ']'\n if (optional) {\n if (i >= len || token[i] !== ']') {\n return { kind: 'static', value: token };\n }\n i++;\n }\n\n // Must be at end of string — no trailing characters\n if (i !== len) {\n return { kind: 'static', value: token };\n }\n\n if (optional && catchAll) return { kind: 'optional-catch-all', name };\n if (catchAll) return { kind: 'catch-all', name };\n if (optional) {\n // '[[name]]' without '...' is malformed — not a valid segment syntax\n return { kind: 'static', value: token };\n }\n return { kind: 'dynamic', name };\n}\n"],"mappings":";;;;;;;;;;;;;;AAsCA,SAAgB,mBAAmB,OAA2B;CAC5D,MAAM,MAAM,MAAM;AAGlB,KAAI,QAAQ,KAAK,MAAM,OAAO,IAC5B,QAAO;EAAE,MAAM;EAAU,OAAO;EAAO;CAGzC,IAAI,IAAI;CAGR,MAAM,WAAW,MAAM,OAAO;AAC9B,KAAI,SAAU;CAGd,MAAM,WAAW,IAAI,IAAI,OAAO,MAAM,OAAO,OAAO,MAAM,IAAI,OAAO,OAAO,MAAM,IAAI,OAAO;AAC7F,KAAI,SAAU,MAAK;CAGnB,MAAM,YAAY;AAClB,QAAO,IAAI,OAAO,MAAM,OAAO,IAAK;AAGpC,KAAI,KAAK,OAAO,MAAM,UACpB,QAAO;EAAE,MAAM;EAAU,OAAO;EAAO;CAGzC,MAAM,OAAO,MAAM,MAAM,WAAW,EAAE;AACtC;AAGA,KAAI,UAAU;AACZ,MAAI,KAAK,OAAO,MAAM,OAAO,IAC3B,QAAO;GAAE,MAAM;GAAU,OAAO;GAAO;AAEzC;;AAIF,KAAI,MAAM,IACR,QAAO;EAAE,MAAM;EAAU,OAAO;EAAO;AAGzC,KAAI,YAAY,SAAU,QAAO;EAAE,MAAM;EAAsB;EAAM;AACrE,KAAI,SAAU,QAAO;EAAE,MAAM;EAAa;EAAM;AAChD,KAAI,SAEF,QAAO;EAAE,MAAM;EAAU,OAAO;EAAO;AAEzC,QAAO;EAAE,MAAM;EAAW;EAAM"}
@@ -1,58 +0,0 @@
1
- /**
2
- * Manifest-compatible status-code file resolver.
3
- *
4
- * The existing status-code-resolver.ts works with SegmentNode (Map-based).
5
- * This module works with ManifestSegmentNode (object-based) for use at
6
- * runtime in the RSC/SSR entries, where the route manifest provides
7
- * plain objects instead of Maps.
8
- *
9
- * Supports two format families:
10
- * - 'component' (default): .tsx/.jsx/.mdx status files → React rendering pipeline
11
- * - 'json': .json status files → raw JSON response, no React
12
- *
13
- * Follows the same fallback chains as status-code-resolver.ts:
14
- *
15
- * **Component chain (4xx):**
16
- * Pass 1 — status files (leaf → root): {status}.tsx → 4xx.tsx
17
- * Pass 2 — legacy compat (leaf → root): not-found.tsx / forbidden.tsx / unauthorized.tsx
18
- * Pass 3 — error.tsx (leaf → root)
19
- * Pass 4 — framework default (returns null)
20
- *
21
- * **JSON chain (4xx):**
22
- * Pass 1 — json status files (leaf → root): {status}.json → 4xx.json
23
- * Pass 2 — framework default JSON (returns null, caller provides bare JSON)
24
- *
25
- * See design/10-error-handling.md §"Status-Code Files"
26
- */
27
- import type { ManifestSegmentNode } from './route-matcher.js';
28
- /** A file reference in the manifest (lazy import + path). */
29
- interface ManifestFile {
30
- load: () => Promise<unknown>;
31
- filePath: string;
32
- }
33
- /** How the status-code file was matched. */
34
- export type ManifestStatusFileKind = 'exact' | 'category' | 'legacy' | 'error';
35
- /** Response format family for status-code resolution. */
36
- export type ManifestStatusFileFormat = 'component' | 'json';
37
- /** Result of resolving a status-code file from manifest segments. */
38
- export interface ManifestStatusFileResolution {
39
- /** The matched manifest file (has load() and filePath). */
40
- file: ManifestFile;
41
- /** The HTTP status code (always the original status, not the file's code). */
42
- status: number;
43
- /** How the file was matched. */
44
- kind: ManifestStatusFileKind;
45
- /** Index into the segments array where the file was found. */
46
- segmentIndex: number;
47
- }
48
- /**
49
- * Resolve the status-code file to render for a given HTTP status code,
50
- * using manifest segment nodes (plain objects, not Maps).
51
- *
52
- * @param status - The HTTP status code (4xx or 5xx).
53
- * @param segments - The matched segment chain from root (index 0) to leaf (last).
54
- * @param format - The response format family ('component' or 'json'). Defaults to 'component'.
55
- */
56
- export declare function resolveManifestStatusFile(status: number, segments: ReadonlyArray<ManifestSegmentNode>, format?: ManifestStatusFileFormat): ManifestStatusFileResolution | null;
57
- export {};
58
- //# sourceMappingURL=manifest-status-resolver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"manifest-status-resolver.d.ts","sourceRoot":"","sources":["../../src/server/manifest-status-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAI9D,6DAA6D;AAC7D,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,4CAA4C;AAC5C,MAAM,MAAM,sBAAsB,GAC9B,OAAO,GACP,UAAU,GACV,QAAQ,GACR,OAAO,CAAC;AAEZ,yDAAyD;AACzD,MAAM,MAAM,wBAAwB,GAAG,WAAW,GAAG,MAAM,CAAC;AAE5D,qEAAqE;AACrE,MAAM,WAAW,4BAA4B;IAC3C,2DAA2D;IAC3D,IAAI,EAAE,YAAY,CAAC;IACnB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,IAAI,EAAE,sBAAsB,CAAC;IAC7B,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;CACtB;AAYD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,aAAa,CAAC,mBAAmB,CAAC,EAC5C,MAAM,GAAE,wBAAsC,GAC7C,4BAA4B,GAAG,IAAI,CAcrC"}
@@ -1,215 +0,0 @@
1
- /**
2
- * Manifest-compatible status-code file resolver.
3
- *
4
- * The existing status-code-resolver.ts works with SegmentNode (Map-based).
5
- * This module works with ManifestSegmentNode (object-based) for use at
6
- * runtime in the RSC/SSR entries, where the route manifest provides
7
- * plain objects instead of Maps.
8
- *
9
- * Supports two format families:
10
- * - 'component' (default): .tsx/.jsx/.mdx status files → React rendering pipeline
11
- * - 'json': .json status files → raw JSON response, no React
12
- *
13
- * Follows the same fallback chains as status-code-resolver.ts:
14
- *
15
- * **Component chain (4xx):**
16
- * Pass 1 — status files (leaf → root): {status}.tsx → 4xx.tsx
17
- * Pass 2 — legacy compat (leaf → root): not-found.tsx / forbidden.tsx / unauthorized.tsx
18
- * Pass 3 — error.tsx (leaf → root)
19
- * Pass 4 — framework default (returns null)
20
- *
21
- * **JSON chain (4xx):**
22
- * Pass 1 — json status files (leaf → root): {status}.json → 4xx.json
23
- * Pass 2 — framework default JSON (returns null, caller provides bare JSON)
24
- *
25
- * See design/10-error-handling.md §"Status-Code Files"
26
- */
27
-
28
- import type { ManifestSegmentNode } from './route-matcher.js';
29
-
30
- // ─── Types ───────────────────────────────────────────────────────────────────
31
-
32
- /** A file reference in the manifest (lazy import + path). */
33
- interface ManifestFile {
34
- load: () => Promise<unknown>;
35
- filePath: string;
36
- }
37
-
38
- /** How the status-code file was matched. */
39
- export type ManifestStatusFileKind =
40
- | 'exact' // e.g. 403.tsx matched status 403
41
- | 'category' // e.g. 4xx.tsx matched status 403
42
- | 'legacy' // e.g. not-found.tsx matched status 404
43
- | 'error'; // error.tsx as last resort
44
-
45
- /** Response format family for status-code resolution. */
46
- export type ManifestStatusFileFormat = 'component' | 'json';
47
-
48
- /** Result of resolving a status-code file from manifest segments. */
49
- export interface ManifestStatusFileResolution {
50
- /** The matched manifest file (has load() and filePath). */
51
- file: ManifestFile;
52
- /** The HTTP status code (always the original status, not the file's code). */
53
- status: number;
54
- /** How the file was matched. */
55
- kind: ManifestStatusFileKind;
56
- /** Index into the segments array where the file was found. */
57
- segmentIndex: number;
58
- }
59
-
60
- // ─── Legacy Compat Mapping ───────────────────────────────────────────────────
61
-
62
- const LEGACY_FILE_TO_STATUS: Record<string, number> = {
63
- 'not-found': 404,
64
- 'forbidden': 403,
65
- 'unauthorized': 401,
66
- };
67
-
68
- // ─── Resolver ────────────────────────────────────────────────────────────────
69
-
70
- /**
71
- * Resolve the status-code file to render for a given HTTP status code,
72
- * using manifest segment nodes (plain objects, not Maps).
73
- *
74
- * @param status - The HTTP status code (4xx or 5xx).
75
- * @param segments - The matched segment chain from root (index 0) to leaf (last).
76
- * @param format - The response format family ('component' or 'json'). Defaults to 'component'.
77
- */
78
- export function resolveManifestStatusFile(
79
- status: number,
80
- segments: ReadonlyArray<ManifestSegmentNode>,
81
- format: ManifestStatusFileFormat = 'component'
82
- ): ManifestStatusFileResolution | null {
83
- if (status < 400 || status > 599) {
84
- return null;
85
- }
86
-
87
- if (format === 'json') {
88
- return resolveJson(status, segments);
89
- }
90
-
91
- if (status <= 499) {
92
- return resolve4xx(status, segments);
93
- }
94
-
95
- return resolve5xx(status, segments);
96
- }
97
-
98
- /**
99
- * 4xx component fallback chain (three separate passes):
100
- * Pass 1 — status files (leaf → root): {status}.tsx → 4xx.tsx
101
- * Pass 2 — legacy compat (leaf → root): not-found.tsx / forbidden.tsx / unauthorized.tsx
102
- * Pass 3 — error.tsx (leaf → root)
103
- */
104
- function resolve4xx(
105
- status: number,
106
- segments: ReadonlyArray<ManifestSegmentNode>
107
- ): ManifestStatusFileResolution | null {
108
- const statusStr = String(status);
109
-
110
- // Pass 1: status files across all segments (leaf → root)
111
- for (let i = segments.length - 1; i >= 0; i--) {
112
- const segment = segments[i];
113
- if (!segment.statusFiles) continue;
114
-
115
- // Exact match first
116
- const exact = segment.statusFiles[statusStr];
117
- if (exact) {
118
- return { file: exact, status, kind: 'exact', segmentIndex: i };
119
- }
120
-
121
- // Category catch-all
122
- const category = segment.statusFiles['4xx'];
123
- if (category) {
124
- return { file: category, status, kind: 'category', segmentIndex: i };
125
- }
126
- }
127
-
128
- // Pass 2: legacy compat files (leaf → root)
129
- for (let i = segments.length - 1; i >= 0; i--) {
130
- const segment = segments[i];
131
- if (!segment.legacyStatusFiles) continue;
132
-
133
- for (const [name, legacyStatus] of Object.entries(LEGACY_FILE_TO_STATUS)) {
134
- if (legacyStatus === status) {
135
- const file = segment.legacyStatusFiles[name];
136
- if (file) {
137
- return { file, status, kind: 'legacy', segmentIndex: i };
138
- }
139
- }
140
- }
141
- }
142
-
143
- // Pass 3: error.tsx (leaf → root)
144
- for (let i = segments.length - 1; i >= 0; i--) {
145
- if (segments[i].error) {
146
- return { file: segments[i].error!, status, kind: 'error', segmentIndex: i };
147
- }
148
- }
149
-
150
- return null;
151
- }
152
-
153
- /**
154
- * 5xx component fallback chain (single pass, per-segment):
155
- * At each segment (leaf → root): {status}.tsx → 5xx.tsx → error.tsx
156
- */
157
- function resolve5xx(
158
- status: number,
159
- segments: ReadonlyArray<ManifestSegmentNode>
160
- ): ManifestStatusFileResolution | null {
161
- const statusStr = String(status);
162
-
163
- for (let i = segments.length - 1; i >= 0; i--) {
164
- const segment = segments[i];
165
-
166
- if (segment.statusFiles) {
167
- const exact = segment.statusFiles[statusStr];
168
- if (exact) {
169
- return { file: exact, status, kind: 'exact', segmentIndex: i };
170
- }
171
-
172
- const categoryKey = '5xx';
173
- const category = segment.statusFiles[categoryKey];
174
- if (category) {
175
- return { file: category, status, kind: 'category', segmentIndex: i };
176
- }
177
- }
178
-
179
- if (segment.error) {
180
- return { file: segment.error, status, kind: 'error', segmentIndex: i };
181
- }
182
- }
183
-
184
- return null;
185
- }
186
-
187
- /**
188
- * JSON fallback chain (for both 4xx and 5xx):
189
- * At each segment (leaf → root): {status}.json → {category}.json
190
- * No legacy compat, no error.tsx — JSON chain terminates at category catch-all.
191
- */
192
- function resolveJson(
193
- status: number,
194
- segments: ReadonlyArray<ManifestSegmentNode>
195
- ): ManifestStatusFileResolution | null {
196
- const statusStr = String(status);
197
- const categoryKey = status >= 500 ? '5xx' : '4xx';
198
-
199
- for (let i = segments.length - 1; i >= 0; i--) {
200
- const segment = segments[i];
201
- if (!segment.jsonStatusFiles) continue;
202
-
203
- const exact = segment.jsonStatusFiles[statusStr];
204
- if (exact) {
205
- return { file: exact, status, kind: 'exact', segmentIndex: i };
206
- }
207
-
208
- const category = segment.jsonStatusFiles[categoryKey];
209
- if (category) {
210
- return { file: category, status, kind: 'category', segmentIndex: i };
211
- }
212
- }
213
-
214
- return null;
215
- }