@cmj/juice 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/cli/commands.d.ts +29 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +102 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/create.d.ts +35 -0
- package/dist/cli/create.d.ts.map +1 -0
- package/dist/cli/create.js +108 -0
- package/dist/cli/create.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +97 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/templates.d.ts +14 -0
- package/dist/cli/templates.d.ts.map +1 -0
- package/dist/cli/templates.js +154 -0
- package/dist/cli/templates.js.map +1 -0
- package/dist/compiler/errors.d.ts +91 -0
- package/dist/compiler/errors.d.ts.map +1 -0
- package/dist/compiler/errors.js +110 -0
- package/dist/compiler/errors.js.map +1 -0
- package/dist/compiler/manifest.d.ts +39 -0
- package/dist/compiler/manifest.d.ts.map +1 -0
- package/dist/compiler/manifest.js +78 -0
- package/dist/compiler/manifest.js.map +1 -0
- package/dist/compiler/parse.d.ts +126 -0
- package/dist/compiler/parse.d.ts.map +1 -0
- package/dist/compiler/parse.js +246 -0
- package/dist/compiler/parse.js.map +1 -0
- package/dist/compiler/plugin.d.ts +43 -0
- package/dist/compiler/plugin.d.ts.map +1 -0
- package/dist/compiler/plugin.js +281 -0
- package/dist/compiler/plugin.js.map +1 -0
- package/dist/compiler/proxy.d.ts +42 -0
- package/dist/compiler/proxy.d.ts.map +1 -0
- package/dist/compiler/proxy.js +80 -0
- package/dist/compiler/proxy.js.map +1 -0
- package/dist/compiler/registry.d.ts +58 -0
- package/dist/compiler/registry.d.ts.map +1 -0
- package/dist/compiler/registry.js +79 -0
- package/dist/compiler/registry.js.map +1 -0
- package/dist/compiler/server-action-registry.d.ts +57 -0
- package/dist/compiler/server-action-registry.d.ts.map +1 -0
- package/dist/compiler/server-action-registry.js +76 -0
- package/dist/compiler/server-action-registry.js.map +1 -0
- package/dist/compiler/server-actions.d.ts +49 -0
- package/dist/compiler/server-actions.d.ts.map +1 -0
- package/dist/compiler/server-actions.js +89 -0
- package/dist/compiler/server-actions.js.map +1 -0
- package/dist/compiler/types.d.ts +188 -0
- package/dist/compiler/types.d.ts.map +1 -0
- package/dist/compiler/types.js +9 -0
- package/dist/compiler/types.js.map +1 -0
- package/dist/runtime/actions.d.ts +37 -0
- package/dist/runtime/actions.d.ts.map +1 -0
- package/dist/runtime/actions.js +167 -0
- package/dist/runtime/actions.js.map +1 -0
- package/dist/runtime/dev.d.ts +43 -0
- package/dist/runtime/dev.d.ts.map +1 -0
- package/dist/runtime/dev.js +260 -0
- package/dist/runtime/dev.js.map +1 -0
- package/dist/runtime/errors.d.ts +98 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +124 -0
- package/dist/runtime/errors.js.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/matcher.d.ts +44 -0
- package/dist/runtime/matcher.d.ts.map +1 -0
- package/dist/runtime/matcher.js +83 -0
- package/dist/runtime/matcher.js.map +1 -0
- package/dist/runtime/render.d.ts +25 -0
- package/dist/runtime/render.d.ts.map +1 -0
- package/dist/runtime/render.js +141 -0
- package/dist/runtime/render.js.map +1 -0
- package/dist/runtime/resolve.d.ts +24 -0
- package/dist/runtime/resolve.d.ts.map +1 -0
- package/dist/runtime/resolve.js +41 -0
- package/dist/runtime/resolve.js.map +1 -0
- package/dist/runtime/router.d.ts +74 -0
- package/dist/runtime/router.d.ts.map +1 -0
- package/dist/runtime/router.js +367 -0
- package/dist/runtime/router.js.map +1 -0
- package/dist/runtime/types.d.ts +245 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +5 -0
- package/dist/runtime/types.js.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// ── juice/runtime dev mode ─────────────────────────────────────────
|
|
2
|
+
// Development-only utilities. WinterCG only. No Node.js APIs.
|
|
3
|
+
//
|
|
4
|
+
// These are internal (`_` prefixed) and never exported from the
|
|
5
|
+
// public `juice/runtime` barrel. They are tree-shaken in production
|
|
6
|
+
// builds when the `mode` option is `'production'` (default).
|
|
7
|
+
// ────────────────────────────────────────────────────────────────────
|
|
8
|
+
/**
|
|
9
|
+
* Generates a rich HTML error overlay for development mode.
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - Dark theme with syntax-highlighted stack trace
|
|
13
|
+
* - Source file + line number parsed from Error stack
|
|
14
|
+
* - Request URL, method, and matched route context
|
|
15
|
+
* - Auto-refresh via HMR EventSource (if hmrUrl provided)
|
|
16
|
+
*
|
|
17
|
+
* @param error - The caught error (may be Error or unknown).
|
|
18
|
+
* @param req - The incoming request (for URL/method display).
|
|
19
|
+
* @param context - Optional context string (e.g., route path, action ID).
|
|
20
|
+
* @returns A full HTML string for the error overlay response.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
export function _generateDevErrorOverlay(error, req, context) {
|
|
25
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
26
|
+
const name = err.name || 'Error';
|
|
27
|
+
const message = _escapeHtml(err.message);
|
|
28
|
+
const stack = _formatStack(err.stack ?? '');
|
|
29
|
+
const url = new URL(req.url);
|
|
30
|
+
const contextLine = context ? `<p class="ctx">Context: ${_escapeHtml(context)}</p>` : '';
|
|
31
|
+
return `<!DOCTYPE html>
|
|
32
|
+
<html lang="en">
|
|
33
|
+
<head>
|
|
34
|
+
<meta charset="utf-8" />
|
|
35
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
36
|
+
<title>🧃 Juice — ${name}</title>
|
|
37
|
+
<style>
|
|
38
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
39
|
+
body{
|
|
40
|
+
font-family:'SF Mono','Cascadia Code','Fira Code',monospace;
|
|
41
|
+
background:#0a0a0f;
|
|
42
|
+
color:#e4e4e7;
|
|
43
|
+
min-height:100vh;
|
|
44
|
+
display:flex;
|
|
45
|
+
flex-direction:column;
|
|
46
|
+
padding:2rem;
|
|
47
|
+
}
|
|
48
|
+
.bar{
|
|
49
|
+
display:flex;
|
|
50
|
+
align-items:center;
|
|
51
|
+
gap:.75rem;
|
|
52
|
+
padding:.75rem 1.25rem;
|
|
53
|
+
background:linear-gradient(135deg,#1a0a2e,#16082a);
|
|
54
|
+
border:1px solid #2d1b4e;
|
|
55
|
+
border-radius:12px;
|
|
56
|
+
margin-bottom:1.5rem;
|
|
57
|
+
}
|
|
58
|
+
.bar .logo{font-size:1.5rem}
|
|
59
|
+
.bar .label{color:#a78bfa;font-size:.85rem;font-weight:600;letter-spacing:.05em}
|
|
60
|
+
.bar .mode{
|
|
61
|
+
margin-left:auto;
|
|
62
|
+
font-size:.7rem;
|
|
63
|
+
padding:3px 10px;
|
|
64
|
+
border-radius:100px;
|
|
65
|
+
background:#7c3aed22;
|
|
66
|
+
color:#a78bfa;
|
|
67
|
+
border:1px solid #7c3aed44;
|
|
68
|
+
}
|
|
69
|
+
.error-box{
|
|
70
|
+
background:#18181b;
|
|
71
|
+
border:1px solid #ef444488;
|
|
72
|
+
border-left:4px solid #ef4444;
|
|
73
|
+
border-radius:12px;
|
|
74
|
+
padding:1.5rem 2rem;
|
|
75
|
+
margin-bottom:1.5rem;
|
|
76
|
+
}
|
|
77
|
+
.error-box h1{
|
|
78
|
+
color:#ef4444;
|
|
79
|
+
font-size:1.25rem;
|
|
80
|
+
font-weight:700;
|
|
81
|
+
margin-bottom:.5rem;
|
|
82
|
+
}
|
|
83
|
+
.error-box .msg{
|
|
84
|
+
color:#fca5a5;
|
|
85
|
+
font-size:.95rem;
|
|
86
|
+
line-height:1.6;
|
|
87
|
+
word-break:break-word;
|
|
88
|
+
}
|
|
89
|
+
.ctx{
|
|
90
|
+
color:#a1a1aa;
|
|
91
|
+
font-size:.8rem;
|
|
92
|
+
margin-top:.75rem;
|
|
93
|
+
padding:.5rem .75rem;
|
|
94
|
+
background:#27272a;
|
|
95
|
+
border-radius:8px;
|
|
96
|
+
}
|
|
97
|
+
.req{
|
|
98
|
+
display:flex;
|
|
99
|
+
gap:1rem;
|
|
100
|
+
margin-bottom:1.5rem;
|
|
101
|
+
}
|
|
102
|
+
.req .pill{
|
|
103
|
+
font-size:.75rem;
|
|
104
|
+
padding:4px 12px;
|
|
105
|
+
border-radius:100px;
|
|
106
|
+
background:#27272a;
|
|
107
|
+
border:1px solid #3f3f46;
|
|
108
|
+
color:#a1a1aa;
|
|
109
|
+
}
|
|
110
|
+
.req .pill b{color:#e4e4e7}
|
|
111
|
+
.stack-box{
|
|
112
|
+
background:#18181b;
|
|
113
|
+
border:1px solid #27272a;
|
|
114
|
+
border-radius:12px;
|
|
115
|
+
padding:1.25rem 1.5rem;
|
|
116
|
+
overflow-x:auto;
|
|
117
|
+
}
|
|
118
|
+
.stack-box h2{
|
|
119
|
+
font-size:.8rem;
|
|
120
|
+
color:#71717a;
|
|
121
|
+
text-transform:uppercase;
|
|
122
|
+
letter-spacing:.1em;
|
|
123
|
+
margin-bottom:.75rem;
|
|
124
|
+
}
|
|
125
|
+
.stack{
|
|
126
|
+
font-size:.8rem;
|
|
127
|
+
line-height:2;
|
|
128
|
+
color:#a1a1aa;
|
|
129
|
+
}
|
|
130
|
+
.stack .fn{color:#93c5fd}
|
|
131
|
+
.stack .file{color:#fbbf24}
|
|
132
|
+
.stack .line-num{color:#ef4444}
|
|
133
|
+
.hint{
|
|
134
|
+
margin-top:1.5rem;
|
|
135
|
+
padding:1rem 1.25rem;
|
|
136
|
+
background:#1c1917;
|
|
137
|
+
border:1px solid #292524;
|
|
138
|
+
border-radius:8px;
|
|
139
|
+
font-size:.75rem;
|
|
140
|
+
color:#78716c;
|
|
141
|
+
line-height:1.6;
|
|
142
|
+
}
|
|
143
|
+
.hint code{color:#a78bfa;font-size:.8rem}
|
|
144
|
+
</style>
|
|
145
|
+
</head>
|
|
146
|
+
<body>
|
|
147
|
+
<div class="bar">
|
|
148
|
+
<span class="logo">🧃</span>
|
|
149
|
+
<span class="label">JUICE RUNTIME ERROR</span>
|
|
150
|
+
<span class="mode">DEV MODE</span>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<div class="req">
|
|
154
|
+
<span class="pill"><b>${_escapeHtml(req.method)}</b></span>
|
|
155
|
+
<span class="pill">${_escapeHtml(url.pathname + url.search)}</span>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div class="error-box">
|
|
159
|
+
<h1>${_escapeHtml(name)}</h1>
|
|
160
|
+
<p class="msg">${message}</p>
|
|
161
|
+
${contextLine}
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div class="stack-box">
|
|
165
|
+
<h2>Stack Trace</h2>
|
|
166
|
+
<div class="stack">${stack || '<em>No stack trace available</em>'}</div>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div class="hint">
|
|
170
|
+
💡 This error overlay is only shown in <code>mode: 'development'</code>.
|
|
171
|
+
In production, a minimal 500 response is returned instead.<br/>
|
|
172
|
+
Save a file to trigger HMR and auto-reload.
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<script type="module">
|
|
176
|
+
// Auto-refresh when Vite signals a full-reload
|
|
177
|
+
if (import.meta.hot) {
|
|
178
|
+
import.meta.hot.on('vite:beforeFullReload', () => location.reload());
|
|
179
|
+
}
|
|
180
|
+
</script>
|
|
181
|
+
</body>
|
|
182
|
+
</html>`;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Generates the Vite HMR client `<script>` tag for SSR injection.
|
|
186
|
+
*
|
|
187
|
+
* @param hmrUrl - The HMR client URL (default: `'/@vite/client'`).
|
|
188
|
+
* @returns A `<script type="module">` string.
|
|
189
|
+
*
|
|
190
|
+
* @internal
|
|
191
|
+
*/
|
|
192
|
+
export function _generateHmrClientScript(hmrUrl) {
|
|
193
|
+
const url = hmrUrl || '/@vite/client';
|
|
194
|
+
return `<script type="module" src="${_escapeHtml(url)}"></script>`;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Appends a cache-busting timestamp query parameter to a module ID.
|
|
198
|
+
*
|
|
199
|
+
* In development mode, this forces the JS runtime to re-evaluate
|
|
200
|
+
* the module on every `import()` call, ensuring edits are picked up
|
|
201
|
+
* immediately without a server restart.
|
|
202
|
+
*
|
|
203
|
+
* In production, this function should NOT be called — modules are
|
|
204
|
+
* cached normally for performance.
|
|
205
|
+
*
|
|
206
|
+
* @param moduleId - The original module path (e.g., `'./app/routes/home.tsx'`).
|
|
207
|
+
* @returns The module path with `?t={timestamp}` appended.
|
|
208
|
+
*
|
|
209
|
+
* @internal
|
|
210
|
+
*/
|
|
211
|
+
export function _bustModuleCache(moduleId) {
|
|
212
|
+
const separator = moduleId.includes('?') ? '&' : '?';
|
|
213
|
+
return `${moduleId}${separator}t=${Date.now()}`;
|
|
214
|
+
}
|
|
215
|
+
// ── Private Helpers ────────────────────────────────────────────────
|
|
216
|
+
/**
|
|
217
|
+
* Escapes HTML special characters to prevent XSS in the error overlay.
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
function _escapeHtml(str) {
|
|
221
|
+
return str
|
|
222
|
+
.replace(/&/g, '&')
|
|
223
|
+
.replace(/</g, '<')
|
|
224
|
+
.replace(/>/g, '>')
|
|
225
|
+
.replace(/"/g, '"')
|
|
226
|
+
.replace(/'/g, ''');
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Parses and syntax-highlights a stack trace for the error overlay.
|
|
230
|
+
* Highlights function names in blue, file paths in yellow, and
|
|
231
|
+
* line numbers in red.
|
|
232
|
+
*
|
|
233
|
+
* @internal
|
|
234
|
+
*/
|
|
235
|
+
function _formatStack(stack) {
|
|
236
|
+
if (!stack)
|
|
237
|
+
return '';
|
|
238
|
+
return stack
|
|
239
|
+
.split('\n')
|
|
240
|
+
.map((line) => {
|
|
241
|
+
// Match " at FunctionName (file:line:col)"
|
|
242
|
+
const match = line.match(/^\s*at\s+(.+?)\s+\((.+?):(\d+):\d+\)/);
|
|
243
|
+
if (match) {
|
|
244
|
+
const fn = match[1];
|
|
245
|
+
const file = match[2];
|
|
246
|
+
const lineNum = match[3];
|
|
247
|
+
return `at <span class="fn">${_escapeHtml(fn)}</span> (<span class="file">${_escapeHtml(file)}</span>:<span class="line-num">${lineNum}</span>)`;
|
|
248
|
+
}
|
|
249
|
+
// Match " at file:line:col" (no function name)
|
|
250
|
+
const match2 = line.match(/^\s*at\s+(.+?):(\d+):\d+/);
|
|
251
|
+
if (match2) {
|
|
252
|
+
const file = match2[1];
|
|
253
|
+
const lineNum = match2[2];
|
|
254
|
+
return `at <span class="file">${_escapeHtml(file)}</span>:<span class="line-num">${lineNum}</span>`;
|
|
255
|
+
}
|
|
256
|
+
return _escapeHtml(line);
|
|
257
|
+
})
|
|
258
|
+
.join('<br/>');
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/runtime/dev.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base error class for all Juice runtime errors.
|
|
3
|
+
*
|
|
4
|
+
* Every error includes:
|
|
5
|
+
* - A `[juice/runtime]` prefix for instant identification in logs.
|
|
6
|
+
* - A machine-readable `code` for programmatic error handling.
|
|
7
|
+
* - An actionable message telling the developer exactly how to fix it.
|
|
8
|
+
*/
|
|
9
|
+
export declare class JuiceRouterError extends Error {
|
|
10
|
+
/** Machine-readable error code (e.g., `'ROUTE_NOT_FOUND'`). */
|
|
11
|
+
readonly code: string;
|
|
12
|
+
name: string;
|
|
13
|
+
constructor(message: string,
|
|
14
|
+
/** Machine-readable error code (e.g., `'ROUTE_NOT_FOUND'`). */
|
|
15
|
+
code: string);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Thrown when the incoming request URL does not match any route
|
|
19
|
+
* in the flight manifest.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```
|
|
23
|
+
* // Error message:
|
|
24
|
+
* // [juice/runtime] No route matches "/dashboard/settings".
|
|
25
|
+
* // Check that your app/ directory has a page.tsx file for this path,
|
|
26
|
+
* // then re-run the Juice compiler to regenerate flight-manifest.json.
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare class RouteNotFoundError extends JuiceRouterError {
|
|
30
|
+
readonly name = "RouteNotFoundError";
|
|
31
|
+
constructor(pathname: string);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Thrown when a POST request references a server action ID that
|
|
35
|
+
* does not exist in the flight manifest.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```
|
|
39
|
+
* // Error message:
|
|
40
|
+
* // [juice/runtime] Server Action "abc123" not found in the manifest.
|
|
41
|
+
* // Ensure the function is exported with 'use server' and the manifest
|
|
42
|
+
* // is up to date.
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare class ActionNotFoundError extends JuiceRouterError {
|
|
46
|
+
readonly name = "ActionNotFoundError";
|
|
47
|
+
constructor(actionId: string);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Thrown when the flight manifest fails structural validation
|
|
51
|
+
* at router initialization time.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```
|
|
55
|
+
* // Error message:
|
|
56
|
+
* // [juice/runtime] Invalid flight-manifest.json: missing "routes" field.
|
|
57
|
+
* // Re-run the Juice compiler to regenerate it.
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare class ManifestValidationError extends JuiceRouterError {
|
|
61
|
+
readonly name = "ManifestValidationError";
|
|
62
|
+
constructor(detail: string);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Thrown when a dynamic `import()` for a route module or server action
|
|
66
|
+
* module fails at runtime.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```
|
|
70
|
+
* // Error message:
|
|
71
|
+
* // [juice/runtime] Failed to load module "./app/routes/missing.tsx"
|
|
72
|
+
* // for route "/missing". The module may have been deleted or the
|
|
73
|
+
* // flight-manifest.json is stale. Re-run the Juice compiler.
|
|
74
|
+
* // Cause: Cannot find module './app/routes/missing.tsx'
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare class ModuleLoadError extends JuiceRouterError {
|
|
78
|
+
readonly name = "ModuleLoadError";
|
|
79
|
+
constructor(moduleId: string, context: string, cause: unknown);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Thrown internally when a user-provided hook (`onBeforeRequest`,
|
|
83
|
+
* `onNotFound`, `onError`) throws an exception. The router catches
|
|
84
|
+
* this to prevent user bugs from crashing the server.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```
|
|
88
|
+
* // Error message:
|
|
89
|
+
* // [juice/runtime] The "onBeforeRequest" hook threw an error.
|
|
90
|
+
* // Your hook function must return a Response or void, not throw.
|
|
91
|
+
* // Cause: Cannot read properties of undefined (reading 'session')
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare class HookExecutionError extends JuiceRouterError {
|
|
95
|
+
readonly name = "HookExecutionError";
|
|
96
|
+
constructor(hookName: string, cause: unknown);
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/runtime/errors.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IAKvC,+DAA+D;aAC/C,IAAI,EAAE,MAAM;IALrB,IAAI,EAAE,MAAM,CAAsB;gBAGzC,OAAO,EAAE,MAAM;IACf,+DAA+D;IAC/C,IAAI,EAAE,MAAM;CAI/B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,kBAAmB,SAAQ,gBAAgB;IACtD,SAAkB,IAAI,wBAAwB;gBAElC,QAAQ,EAAE,MAAM;CAQ7B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,mBAAoB,SAAQ,gBAAgB;IACvD,SAAkB,IAAI,yBAAyB;gBAEnC,QAAQ,EAAE,MAAM;CAO7B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,uBAAwB,SAAQ,gBAAgB;IAC3D,SAAkB,IAAI,6BAA6B;gBAEvC,MAAM,EAAE,MAAM;CAO3B;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,eAAgB,SAAQ,gBAAgB;IACnD,SAAkB,IAAI,qBAAqB;gBAGzC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO;CAWjB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,kBAAmB,SAAQ,gBAAgB;IACtD,SAAkB,IAAI,wBAAwB;gBAElC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAU7C"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// ── juice/runtime errors ───────────────────────────────────────────
|
|
2
|
+
// Empathic error handling: every error tells the developer
|
|
3
|
+
// WHAT went wrong, WHY, and HOW to fix it.
|
|
4
|
+
// ────────────────────────────────────────────────────────────────────
|
|
5
|
+
/**
|
|
6
|
+
* Base error class for all Juice runtime errors.
|
|
7
|
+
*
|
|
8
|
+
* Every error includes:
|
|
9
|
+
* - A `[juice/runtime]` prefix for instant identification in logs.
|
|
10
|
+
* - A machine-readable `code` for programmatic error handling.
|
|
11
|
+
* - An actionable message telling the developer exactly how to fix it.
|
|
12
|
+
*/
|
|
13
|
+
export class JuiceRouterError extends Error {
|
|
14
|
+
code;
|
|
15
|
+
name = 'JuiceRouterError';
|
|
16
|
+
constructor(message,
|
|
17
|
+
/** Machine-readable error code (e.g., `'ROUTE_NOT_FOUND'`). */
|
|
18
|
+
code) {
|
|
19
|
+
super(`[juice/runtime] ${message}`);
|
|
20
|
+
this.code = code;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Thrown when the incoming request URL does not match any route
|
|
25
|
+
* in the flight manifest.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```
|
|
29
|
+
* // Error message:
|
|
30
|
+
* // [juice/runtime] No route matches "/dashboard/settings".
|
|
31
|
+
* // Check that your app/ directory has a page.tsx file for this path,
|
|
32
|
+
* // then re-run the Juice compiler to regenerate flight-manifest.json.
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export class RouteNotFoundError extends JuiceRouterError {
|
|
36
|
+
name = 'RouteNotFoundError';
|
|
37
|
+
constructor(pathname) {
|
|
38
|
+
super(`No route matches "${pathname}". ` +
|
|
39
|
+
`Check that your app/ directory has a page.tsx file for this path, ` +
|
|
40
|
+
`then re-run the Juice compiler to regenerate flight-manifest.json.`, 'ROUTE_NOT_FOUND');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Thrown when a POST request references a server action ID that
|
|
45
|
+
* does not exist in the flight manifest.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```
|
|
49
|
+
* // Error message:
|
|
50
|
+
* // [juice/runtime] Server Action "abc123" not found in the manifest.
|
|
51
|
+
* // Ensure the function is exported with 'use server' and the manifest
|
|
52
|
+
* // is up to date.
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export class ActionNotFoundError extends JuiceRouterError {
|
|
56
|
+
name = 'ActionNotFoundError';
|
|
57
|
+
constructor(actionId) {
|
|
58
|
+
super(`Server Action "${actionId}" not found in the manifest. ` +
|
|
59
|
+
`Ensure the function is exported with 'use server' and the manifest is up to date.`, 'ACTION_NOT_FOUND');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Thrown when the flight manifest fails structural validation
|
|
64
|
+
* at router initialization time.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```
|
|
68
|
+
* // Error message:
|
|
69
|
+
* // [juice/runtime] Invalid flight-manifest.json: missing "routes" field.
|
|
70
|
+
* // Re-run the Juice compiler to regenerate it.
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export class ManifestValidationError extends JuiceRouterError {
|
|
74
|
+
name = 'ManifestValidationError';
|
|
75
|
+
constructor(detail) {
|
|
76
|
+
super(`Invalid flight-manifest.json: ${detail}. ` +
|
|
77
|
+
`Re-run the Juice compiler to regenerate it.`, 'INVALID_MANIFEST');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Thrown when a dynamic `import()` for a route module or server action
|
|
82
|
+
* module fails at runtime.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```
|
|
86
|
+
* // Error message:
|
|
87
|
+
* // [juice/runtime] Failed to load module "./app/routes/missing.tsx"
|
|
88
|
+
* // for route "/missing". The module may have been deleted or the
|
|
89
|
+
* // flight-manifest.json is stale. Re-run the Juice compiler.
|
|
90
|
+
* // Cause: Cannot find module './app/routes/missing.tsx'
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export class ModuleLoadError extends JuiceRouterError {
|
|
94
|
+
name = 'ModuleLoadError';
|
|
95
|
+
constructor(moduleId, context, cause) {
|
|
96
|
+
const causeMessage = cause instanceof Error ? cause.message : String(cause);
|
|
97
|
+
super(`Failed to load module "${moduleId}" for ${context}. ` +
|
|
98
|
+
`The module may have been deleted or the flight-manifest.json is stale. ` +
|
|
99
|
+
`Re-run the Juice compiler.\nCause: ${causeMessage}`, 'MODULE_LOAD_FAILED');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Thrown internally when a user-provided hook (`onBeforeRequest`,
|
|
104
|
+
* `onNotFound`, `onError`) throws an exception. The router catches
|
|
105
|
+
* this to prevent user bugs from crashing the server.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```
|
|
109
|
+
* // Error message:
|
|
110
|
+
* // [juice/runtime] The "onBeforeRequest" hook threw an error.
|
|
111
|
+
* // Your hook function must return a Response or void, not throw.
|
|
112
|
+
* // Cause: Cannot read properties of undefined (reading 'session')
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export class HookExecutionError extends JuiceRouterError {
|
|
116
|
+
name = 'HookExecutionError';
|
|
117
|
+
constructor(hookName, cause) {
|
|
118
|
+
const causeMessage = cause instanceof Error ? cause.message : String(cause);
|
|
119
|
+
super(`The "${hookName}" hook threw an error. ` +
|
|
120
|
+
`Your hook function must return a Response or void, not throw.\n` +
|
|
121
|
+
`Cause: ${causeMessage}`, 'HOOK_EXECUTION_FAILED');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/runtime/errors.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,2DAA2D;AAC3D,2CAA2C;AAC3C,uEAAuE;AAEvE;;;;;;;GAOG;AACH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAMvB;IALT,IAAI,GAAW,kBAAkB,CAAC;IAE3C,YACE,OAAe;IACf,+DAA+D;IAC/C,IAAY;QAE5B,KAAK,CAAC,mBAAmB,OAAO,EAAE,CAAC,CAAC;QAFpB,SAAI,GAAJ,IAAI,CAAQ;IAG9B,CAAC;CACF;AAED;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,kBAAmB,SAAQ,gBAAgB;IACpC,IAAI,GAAG,oBAAoB,CAAC;IAE9C,YAAY,QAAgB;QAC1B,KAAK,CACH,qBAAqB,QAAQ,KAAK;YAChC,oEAAoE;YACpE,oEAAoE,EACtE,iBAAiB,CAClB,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,mBAAoB,SAAQ,gBAAgB;IACrC,IAAI,GAAG,qBAAqB,CAAC;IAE/C,YAAY,QAAgB;QAC1B,KAAK,CACH,kBAAkB,QAAQ,+BAA+B;YACvD,mFAAmF,EACrF,kBAAkB,CACnB,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;;;;GAUG;AACH,MAAM,OAAO,uBAAwB,SAAQ,gBAAgB;IACzC,IAAI,GAAG,yBAAyB,CAAC;IAEnD,YAAY,MAAc;QACxB,KAAK,CACH,iCAAiC,MAAM,IAAI;YACzC,6CAA6C,EAC/C,kBAAkB,CACnB,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,eAAgB,SAAQ,gBAAgB;IACjC,IAAI,GAAG,iBAAiB,CAAC;IAE3C,YACE,QAAgB,EAChB,OAAe,EACf,KAAc;QAEd,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzD,KAAK,CACH,0BAA0B,QAAQ,SAAS,OAAO,IAAI;YACpD,yEAAyE;YACzE,sCAAsC,YAAY,EAAE,EACtD,oBAAoB,CACrB,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,kBAAmB,SAAQ,gBAAgB;IACpC,IAAI,GAAG,oBAAoB,CAAC;IAE9C,YAAY,QAAgB,EAAE,KAAc;QAC1C,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzD,KAAK,CACH,QAAQ,QAAQ,yBAAyB;YACvC,iEAAiE;YACjE,UAAU,YAAY,EAAE,EAC1B,uBAAuB,CACxB,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// ── juice/runtime ──────────────────────────────────────────────────
|
|
2
|
+
// Public API barrel. Three exports. One function, two types.
|
|
3
|
+
// ────────────────────────────────────────────────────────────────────
|
|
4
|
+
export { createRouter } from './router.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,6DAA6D;AAC7D,uEAAuE;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { RouteEntry, _CompiledRoute, _RouteMatch } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Compiles the manifest's flat route map into an array of `URLPattern`
|
|
4
|
+
* matchers. Called **once** inside `createRouter` at initialization.
|
|
5
|
+
*
|
|
6
|
+
* Patterns use the URLPattern pathname syntax:
|
|
7
|
+
* - `/blog/:slug` — named parameter
|
|
8
|
+
* - `/docs/*` — wildcard
|
|
9
|
+
* - `/` — exact root match
|
|
10
|
+
*
|
|
11
|
+
* @param routes - The `manifest.routes` record (pattern → RouteEntry).
|
|
12
|
+
* @param basePath - Optional prefix prepended to every pattern.
|
|
13
|
+
* @returns An array of compiled routes, ordered by declaration order.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const compiled = _compileRoutes(manifest.routes, '/');
|
|
18
|
+
* // compiled[0].pattern matches URLPattern({ pathname: '/blog/:slug' })
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export declare function _compileRoutes(routes: Readonly<Record<string, RouteEntry>>, basePath: string): _CompiledRoute[];
|
|
24
|
+
/**
|
|
25
|
+
* Matches a full URL string against the compiled route table.
|
|
26
|
+
* Returns the first match with extracted pathname params, or `null`.
|
|
27
|
+
*
|
|
28
|
+
* @param url - The full request URL (e.g., `'https://example.com/blog/hello'`).
|
|
29
|
+
* @param compiled - The pre-compiled route array from `_compileRoutes`.
|
|
30
|
+
* @returns The matched route entry and extracted params, or `null`.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const match = _matchRoute('https://example.com/blog/hello', compiled);
|
|
35
|
+
* if (match) {
|
|
36
|
+
* console.log(match.params.slug); // 'hello'
|
|
37
|
+
* console.log(match.entry.moduleId); // './app/blog/[slug]/page.tsx'
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
43
|
+
export declare function _matchRoute(url: string, compiled: readonly _CompiledRoute[]): _RouteMatch | null;
|
|
44
|
+
//# sourceMappingURL=matcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../../src/runtime/matcher.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG1E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,EAC5C,QAAQ,EAAE,MAAM,GACf,cAAc,EAAE,CAmBlB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,SAAS,cAAc,EAAE,GAClC,WAAW,GAAG,IAAI,CAqBpB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// ── juice/runtime matcher ──────────────────────────────────────────
|
|
2
|
+
// Zero-dependency route matching using the WinterCG URLPattern API.
|
|
3
|
+
// Compiled once at startup, matched per-request.
|
|
4
|
+
// ────────────────────────────────────────────────────────────────────
|
|
5
|
+
import { ManifestValidationError } from './errors.js';
|
|
6
|
+
/**
|
|
7
|
+
* Compiles the manifest's flat route map into an array of `URLPattern`
|
|
8
|
+
* matchers. Called **once** inside `createRouter` at initialization.
|
|
9
|
+
*
|
|
10
|
+
* Patterns use the URLPattern pathname syntax:
|
|
11
|
+
* - `/blog/:slug` — named parameter
|
|
12
|
+
* - `/docs/*` — wildcard
|
|
13
|
+
* - `/` — exact root match
|
|
14
|
+
*
|
|
15
|
+
* @param routes - The `manifest.routes` record (pattern → RouteEntry).
|
|
16
|
+
* @param basePath - Optional prefix prepended to every pattern.
|
|
17
|
+
* @returns An array of compiled routes, ordered by declaration order.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const compiled = _compileRoutes(manifest.routes, '/');
|
|
22
|
+
* // compiled[0].pattern matches URLPattern({ pathname: '/blog/:slug' })
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
export function _compileRoutes(routes, basePath) {
|
|
28
|
+
// Normalize: strip trailing slash from basePath (unless it IS '/')
|
|
29
|
+
const base = basePath === '/' ? '' : basePath.replace(/\/+$/, '');
|
|
30
|
+
return Object.entries(routes).map(([pattern, entry]) => {
|
|
31
|
+
const fullPattern = base + (pattern === '/' ? '/' : pattern);
|
|
32
|
+
try {
|
|
33
|
+
return {
|
|
34
|
+
pattern: new URLPattern({ pathname: fullPattern }),
|
|
35
|
+
entry,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (cause) {
|
|
39
|
+
throw new ManifestValidationError(`invalid route pattern "${pattern}" (resolved to "${fullPattern}"). ` +
|
|
40
|
+
`URLPattern could not parse it. ` +
|
|
41
|
+
`Check the route keys in your manifest`);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Matches a full URL string against the compiled route table.
|
|
47
|
+
* Returns the first match with extracted pathname params, or `null`.
|
|
48
|
+
*
|
|
49
|
+
* @param url - The full request URL (e.g., `'https://example.com/blog/hello'`).
|
|
50
|
+
* @param compiled - The pre-compiled route array from `_compileRoutes`.
|
|
51
|
+
* @returns The matched route entry and extracted params, or `null`.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const match = _matchRoute('https://example.com/blog/hello', compiled);
|
|
56
|
+
* if (match) {
|
|
57
|
+
* console.log(match.params.slug); // 'hello'
|
|
58
|
+
* console.log(match.entry.moduleId); // './app/blog/[slug]/page.tsx'
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @internal
|
|
63
|
+
*/
|
|
64
|
+
export function _matchRoute(url, compiled) {
|
|
65
|
+
for (const route of compiled) {
|
|
66
|
+
const result = route.pattern.exec(url);
|
|
67
|
+
if (result) {
|
|
68
|
+
// URLPattern groups are Record<string, string | undefined>.
|
|
69
|
+
// Filter out undefined values for a clean params object.
|
|
70
|
+
const rawGroups = result.pathname.groups;
|
|
71
|
+
const params = {};
|
|
72
|
+
for (const key of Object.keys(rawGroups)) {
|
|
73
|
+
const value = rawGroups[key];
|
|
74
|
+
if (value !== undefined) {
|
|
75
|
+
params[key] = value;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { entry: route.entry, params };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=matcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matcher.js","sourceRoot":"","sources":["../../src/runtime/matcher.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,oEAAoE;AACpE,iDAAiD;AACjD,uEAAuE;AAGvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,cAAc,CAC5B,MAA4C,EAC5C,QAAgB;IAEhB,mEAAmE;IACnE,MAAM,IAAI,GAAG,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAElE,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE;QACrD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC7D,IAAI,CAAC;YACH,OAAO;gBACL,OAAO,EAAE,IAAI,UAAU,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;gBAClD,KAAK;aACN,CAAC;QACJ,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,IAAI,uBAAuB,CAC/B,0BAA0B,OAAO,mBAAmB,WAAW,MAAM;gBACnE,iCAAiC;gBACjC,uCAAuC,CAC1C,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,WAAW,CACzB,GAAW,EACX,QAAmC;IAEnC,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,MAAM,EAAE,CAAC;YACX,4DAA4D;YAC5D,yDAAyD;YACzD,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACzC,MAAM,MAAM,GAA2B,EAAE,CAAC;YAE1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzC,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;gBAC7B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;gBACtB,CAAC;YACH,CAAC;YAED,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QACxC,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|