@timber-js/app 0.1.9 → 0.1.11
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/dist/_chunks/request-context-BzES06i1.js.map +1 -1
- package/dist/_chunks/{use-query-states-Dd9PVu-L.js → use-query-states-wEXY2JQB.js} +9 -2
- package/dist/_chunks/{use-query-states-Dd9PVu-L.js.map → use-query-states-wEXY2JQB.js.map} +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/search-params/index.js +1 -1
- package/dist/search-params/index.js.map +1 -1
- package/dist/server/fallback-error.d.ts +28 -0
- package/dist/server/fallback-error.d.ts.map +1 -0
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/pipeline.d.ts +12 -0
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/package.json +23 -23
- package/src/client/browser-entry.ts +1 -1
- package/src/client/use-query-states.ts +13 -1
- package/src/index.ts +16 -16
- package/src/plugins/dev-server.ts +3 -1
- package/src/plugins/entries.ts +2 -1
- package/src/plugins/routing.ts +5 -4
- package/src/routing/status-file-lint.ts +1 -3
- package/src/search-params/create.ts +4 -1
- package/src/server/error-formatter.ts +2 -2
- package/src/server/fallback-error.ts +159 -0
- package/src/server/html-injectors.ts +9 -4
- package/src/server/pipeline.ts +24 -0
- package/src/server/request-context.ts +0 -1
- package/src/server/route-matcher.ts +63 -28
- package/src/server/rsc-entry/index.ts +64 -36
- package/src/server/slot-resolver.ts +38 -5
- package/src/server/ssr-entry.ts +4 -1
- package/src/server/tree-builder.ts +4 -1
- package/src/shims/server-only-noop.js +1 -0
|
@@ -10,10 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import type { RouteMatch } from './pipeline.js';
|
|
12
12
|
import type { MiddlewareFn } from './middleware-runner.js';
|
|
13
|
-
import {
|
|
14
|
-
METADATA_ROUTE_CONVENTIONS,
|
|
15
|
-
type MetadataRouteType,
|
|
16
|
-
} from './metadata-routes.js';
|
|
13
|
+
import { METADATA_ROUTE_CONVENTIONS, type MetadataRouteType } from './metadata-routes.js';
|
|
17
14
|
|
|
18
15
|
// ─── Manifest Types ───────────────────────────────────────────────────────
|
|
19
16
|
// The virtual module manifest has a slightly different shape than SegmentNode:
|
|
@@ -127,6 +124,43 @@ function matchPathname(root: ManifestSegmentNode, pathname: string): RouteMatch
|
|
|
127
124
|
};
|
|
128
125
|
}
|
|
129
126
|
|
|
127
|
+
/**
|
|
128
|
+
* An effective child flattened through group segments.
|
|
129
|
+
* Includes the chain of group nodes that must be added to the segments
|
|
130
|
+
* array before the child itself (to preserve the group → child nesting
|
|
131
|
+
* that the renderer expects).
|
|
132
|
+
*/
|
|
133
|
+
interface EffectiveChild {
|
|
134
|
+
child: ManifestSegmentNode;
|
|
135
|
+
groupChain: ManifestSegmentNode[];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Collect effective children by flattening through group segments.
|
|
140
|
+
*
|
|
141
|
+
* Groups are transparent for URL matching — their non-group descendants
|
|
142
|
+
* are returned with the chain of group nodes that lead to them. This
|
|
143
|
+
* allows the caller to apply priority ordering (static > dynamic > ...)
|
|
144
|
+
* across all groups uniformly instead of per-group.
|
|
145
|
+
*/
|
|
146
|
+
function collectEffectiveChildren(
|
|
147
|
+
node: ManifestSegmentNode,
|
|
148
|
+
groupChain: ManifestSegmentNode[] = []
|
|
149
|
+
): EffectiveChild[] {
|
|
150
|
+
const result: EffectiveChild[] = [];
|
|
151
|
+
for (const child of node.children) {
|
|
152
|
+
if (child.segmentType === 'group') {
|
|
153
|
+
// Look through the group — its children become effective children
|
|
154
|
+
// with this group prepended to their chain
|
|
155
|
+
const nested = collectEffectiveChildren(child, [...groupChain, child]);
|
|
156
|
+
result.push(...nested);
|
|
157
|
+
} else {
|
|
158
|
+
result.push({ child, groupChain });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
|
|
130
164
|
/**
|
|
131
165
|
* Recursively match URL segments against the segment tree.
|
|
132
166
|
*
|
|
@@ -136,8 +170,10 @@ function matchPathname(root: ManifestSegmentNode, pathname: string): RouteMatch
|
|
|
136
170
|
* 3. Catch-all segments ([...param])
|
|
137
171
|
* 4. Optional catch-all segments ([[...param]])
|
|
138
172
|
*
|
|
139
|
-
* Groups are transparent — they don't consume URL segments
|
|
140
|
-
*
|
|
173
|
+
* Groups are transparent — they don't consume URL segments. Children
|
|
174
|
+
* are flattened through groups so that priority ordering applies across
|
|
175
|
+
* all groups uniformly (a static in group A always beats a dynamic in
|
|
176
|
+
* group B, regardless of group ordering).
|
|
141
177
|
*/
|
|
142
178
|
function matchSegments(
|
|
143
179
|
node: ManifestSegmentNode,
|
|
@@ -163,11 +199,12 @@ function matchSegments(
|
|
|
163
199
|
}
|
|
164
200
|
}
|
|
165
201
|
|
|
166
|
-
// Check optional catch-all children (
|
|
167
|
-
|
|
202
|
+
// Check optional catch-all children (direct and through groups)
|
|
203
|
+
const effective = collectEffectiveChildren(node);
|
|
204
|
+
for (const { child, groupChain } of effective) {
|
|
168
205
|
if (child.segmentType === 'optional-catch-all') {
|
|
169
206
|
if (child.page || child.route) {
|
|
170
|
-
segments.push(child);
|
|
207
|
+
segments.push(...groupChain, child);
|
|
171
208
|
// Zero segments → param is undefined (not set), matching Next.js semantics
|
|
172
209
|
return true;
|
|
173
210
|
}
|
|
@@ -180,35 +217,33 @@ function matchSegments(
|
|
|
180
217
|
|
|
181
218
|
const part = parts[index];
|
|
182
219
|
|
|
183
|
-
//
|
|
220
|
+
// Flatten children through groups so priority ordering applies globally
|
|
221
|
+
// across all groups, not per-group.
|
|
222
|
+
const effective = collectEffectiveChildren(node);
|
|
184
223
|
|
|
185
224
|
// 1. Static segments
|
|
186
|
-
for (const child of
|
|
225
|
+
for (const { child, groupChain } of effective) {
|
|
187
226
|
if (child.segmentType === 'static' && child.segmentName === part) {
|
|
227
|
+
segments.push(...groupChain);
|
|
188
228
|
if (matchSegments(child, parts, index + 1, segments, params)) {
|
|
189
229
|
return true;
|
|
190
230
|
}
|
|
231
|
+
// Backtrack group chain
|
|
232
|
+
segments.length -= groupChain.length;
|
|
191
233
|
}
|
|
192
234
|
}
|
|
193
235
|
|
|
194
|
-
// 2.
|
|
195
|
-
for (const child of
|
|
196
|
-
if (child.segmentType === 'group') {
|
|
197
|
-
if (matchSegments(child, parts, index, segments, params)) {
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// 3. Dynamic segments ([param])
|
|
204
|
-
for (const child of node.children) {
|
|
236
|
+
// 2. Dynamic segments ([param])
|
|
237
|
+
for (const { child, groupChain } of effective) {
|
|
205
238
|
if (child.segmentType === 'dynamic' && child.paramName) {
|
|
239
|
+
segments.push(...groupChain);
|
|
206
240
|
const prevParam = params[child.paramName];
|
|
207
241
|
params[child.paramName] = part;
|
|
208
242
|
if (matchSegments(child, parts, index + 1, segments, params)) {
|
|
209
243
|
return true;
|
|
210
244
|
}
|
|
211
245
|
// Backtrack
|
|
246
|
+
segments.length -= groupChain.length;
|
|
212
247
|
if (prevParam !== undefined) {
|
|
213
248
|
params[child.paramName] = prevParam;
|
|
214
249
|
} else {
|
|
@@ -217,24 +252,24 @@ function matchSegments(
|
|
|
217
252
|
}
|
|
218
253
|
}
|
|
219
254
|
|
|
220
|
-
//
|
|
221
|
-
for (const child of
|
|
255
|
+
// 3. Catch-all segments ([...param])
|
|
256
|
+
for (const { child, groupChain } of effective) {
|
|
222
257
|
if (child.segmentType === 'catch-all' && child.paramName) {
|
|
223
258
|
if (child.page || child.route) {
|
|
224
259
|
const remaining = parts.slice(index);
|
|
225
|
-
segments.push(child);
|
|
260
|
+
segments.push(...groupChain, child);
|
|
226
261
|
params[child.paramName] = remaining;
|
|
227
262
|
return true;
|
|
228
263
|
}
|
|
229
264
|
}
|
|
230
265
|
}
|
|
231
266
|
|
|
232
|
-
//
|
|
233
|
-
for (const child of
|
|
267
|
+
// 4. Optional catch-all segments ([[...param]])
|
|
268
|
+
for (const { child, groupChain } of effective) {
|
|
234
269
|
if (child.segmentType === 'optional-catch-all' && child.paramName) {
|
|
235
270
|
if (child.page || child.route) {
|
|
236
271
|
const remaining = parts.slice(index);
|
|
237
|
-
segments.push(child);
|
|
272
|
+
segments.push(...groupChain, child);
|
|
238
273
|
params[child.paramName] = remaining;
|
|
239
274
|
return true;
|
|
240
275
|
}
|
|
@@ -24,49 +24,49 @@ import buildManifest from 'virtual:timber-build-manifest';
|
|
|
24
24
|
|
|
25
25
|
import { renderToReadableStream } from '@vitejs/plugin-rsc/rsc';
|
|
26
26
|
|
|
27
|
-
import
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import type {
|
|
31
|
-
import { logRenderError } from '#/server/logger.js';
|
|
32
|
-
import { resolveLogMode } from '#/server/dev-logger.js';
|
|
33
|
-
import { createRouteMatcher, createMetadataRouteMatcher } from '#/server/route-matcher.js';
|
|
34
|
-
import type { ManifestSegmentNode } from '#/server/route-matcher.js';
|
|
35
|
-
import { DenySignal, RedirectSignal, RenderError, SsrStreamError } from '#/server/primitives.js';
|
|
36
|
-
import { buildClientScripts } from '#/server/html-injectors.js';
|
|
37
|
-
import type { ClientBootstrapConfig } from '#/server/html-injectors.js';
|
|
38
|
-
import { renderDenyPage, renderDenyPageAsRsc } from '#/server/deny-renderer.js';
|
|
39
|
-
import type { LayoutEntry } from '#/server/deny-renderer.js';
|
|
27
|
+
import type { FormRerender } from '#/server/action-handler.js';
|
|
28
|
+
import { handleActionRequest, isActionRequest } from '#/server/action-handler.js';
|
|
29
|
+
import type { BodyLimitsConfig } from '#/server/body-limits.js';
|
|
30
|
+
import type { BuildManifest } from '#/server/build-manifest.js';
|
|
40
31
|
import {
|
|
41
|
-
collectRouteCss,
|
|
42
|
-
collectRouteFonts,
|
|
43
|
-
collectRouteModulepreloads,
|
|
44
32
|
buildCssLinkTags,
|
|
45
33
|
buildFontPreloadTags,
|
|
46
34
|
buildModulepreloadTags,
|
|
35
|
+
collectRouteCss,
|
|
36
|
+
collectRouteFonts,
|
|
37
|
+
collectRouteModulepreloads,
|
|
47
38
|
} from '#/server/build-manifest.js';
|
|
48
|
-
import type {
|
|
49
|
-
import {
|
|
39
|
+
import type { LayoutEntry } from '#/server/deny-renderer.js';
|
|
40
|
+
import { renderDenyPage, renderDenyPageAsRsc } from '#/server/deny-renderer.js';
|
|
41
|
+
import { resolveLogMode } from '#/server/dev-logger.js';
|
|
50
42
|
import { sendEarlyHints103 } from '#/server/early-hints-sender.js';
|
|
51
|
-
import
|
|
52
|
-
import { buildRouteElement, RouteSignalWithContext } from '#/server/route-element-builder.js';
|
|
53
|
-
import { isActionRequest, handleActionRequest } from '#/server/action-handler.js';
|
|
54
|
-
import type { FormRerender } from '#/server/action-handler.js';
|
|
55
|
-
import type { BodyLimitsConfig } from '#/server/body-limits.js';
|
|
43
|
+
import { collectEarlyHintHeaders } from '#/server/early-hints.js';
|
|
56
44
|
import { runWithFormFlash } from '#/server/form-flash.js';
|
|
45
|
+
import type { ClientBootstrapConfig } from '#/server/html-injectors.js';
|
|
46
|
+
import { buildClientScripts } from '#/server/html-injectors.js';
|
|
47
|
+
import { logRenderError } from '#/server/logger.js';
|
|
48
|
+
import type { InterceptionContext, PipelineConfig, RouteMatch } from '#/server/pipeline.js';
|
|
49
|
+
import { createPipeline } from '#/server/pipeline.js';
|
|
50
|
+
import { DenySignal, RedirectSignal, RenderError, SsrStreamError } from '#/server/primitives.js';
|
|
51
|
+
import { buildRouteElement, RouteSignalWithContext } from '#/server/route-element-builder.js';
|
|
52
|
+
import type { ManifestSegmentNode } from '#/server/route-matcher.js';
|
|
53
|
+
import { createMetadataRouteMatcher, createRouteMatcher } from '#/server/route-matcher.js';
|
|
54
|
+
import type { NavContext } from '#/server/ssr-entry.js';
|
|
55
|
+
import { initDevTracing } from '#/server/tracing.js';
|
|
57
56
|
|
|
57
|
+
import { renderFallbackError as renderFallback } from '#/server/fallback-error.js';
|
|
58
|
+
import { handleApiRoute } from './api-handler.js';
|
|
59
|
+
import { renderErrorPage, renderNoMatchPage } from './error-renderer.js';
|
|
58
60
|
import {
|
|
59
|
-
createDebugChannelSink,
|
|
60
|
-
buildSegmentInfo,
|
|
61
|
-
isRscPayloadRequest,
|
|
62
61
|
buildRedirectResponse,
|
|
62
|
+
buildSegmentInfo,
|
|
63
|
+
createDebugChannelSink,
|
|
63
64
|
escapeHtml,
|
|
64
65
|
isAbortError,
|
|
66
|
+
isRscPayloadRequest,
|
|
65
67
|
parseCookiesFromHeader,
|
|
66
68
|
RSC_CONTENT_TYPE,
|
|
67
69
|
} from './helpers.js';
|
|
68
|
-
import { handleApiRoute } from './api-handler.js';
|
|
69
|
-
import { renderErrorPage, renderNoMatchPage } from './error-renderer.js';
|
|
70
70
|
import { callSsr } from './ssr-bridge.js';
|
|
71
71
|
|
|
72
72
|
// Dev-only pipeline error handler, set by the dev server after import.
|
|
@@ -183,6 +183,8 @@ async function createRequestHandler(manifest: typeof routeManifest, runtimeConfi
|
|
|
183
183
|
if (_devPipelineErrorHandler) _devPipelineErrorHandler(error, phase);
|
|
184
184
|
}
|
|
185
185
|
: undefined,
|
|
186
|
+
renderFallbackError: (error, req, responseHeaders) =>
|
|
187
|
+
renderFallback(error, req, responseHeaders, isDev, manifest.root, clientBootstrap),
|
|
186
188
|
};
|
|
187
189
|
|
|
188
190
|
const pipeline = createPipeline(pipelineConfig);
|
|
@@ -664,15 +666,28 @@ async function renderRoute(
|
|
|
664
666
|
if (sig) return buildRedirectResponse(_req, sig, responseHeaders);
|
|
665
667
|
if (denySignal) {
|
|
666
668
|
return renderDenyPage(
|
|
667
|
-
denySignal,
|
|
668
|
-
|
|
669
|
+
denySignal,
|
|
670
|
+
segments,
|
|
671
|
+
layoutComponents as LayoutEntry[],
|
|
672
|
+
_req,
|
|
673
|
+
match,
|
|
674
|
+
responseHeaders,
|
|
675
|
+
clientBootstrap,
|
|
676
|
+
createDebugChannelSink,
|
|
677
|
+
callSsr
|
|
669
678
|
);
|
|
670
679
|
}
|
|
671
680
|
const err = renderError as { error: unknown; status: number } | null;
|
|
672
681
|
if (err) {
|
|
673
682
|
return renderErrorPage(
|
|
674
|
-
err.error,
|
|
675
|
-
|
|
683
|
+
err.error,
|
|
684
|
+
err.status,
|
|
685
|
+
segments,
|
|
686
|
+
layoutComponents as LayoutEntry[],
|
|
687
|
+
_req,
|
|
688
|
+
match,
|
|
689
|
+
responseHeaders,
|
|
690
|
+
clientBootstrap
|
|
676
691
|
);
|
|
677
692
|
}
|
|
678
693
|
return null;
|
|
@@ -712,15 +727,28 @@ async function renderRoute(
|
|
|
712
727
|
if (denySignal) {
|
|
713
728
|
// Render deny page without layouts — pass empty layout list
|
|
714
729
|
return renderDenyPage(
|
|
715
|
-
denySignal,
|
|
716
|
-
|
|
730
|
+
denySignal,
|
|
731
|
+
segments,
|
|
732
|
+
[] as LayoutEntry[],
|
|
733
|
+
_req,
|
|
734
|
+
match,
|
|
735
|
+
responseHeaders,
|
|
736
|
+
clientBootstrap,
|
|
737
|
+
createDebugChannelSink,
|
|
738
|
+
callSsr
|
|
717
739
|
);
|
|
718
740
|
}
|
|
719
741
|
const err = renderError as { error: unknown; status: number } | null;
|
|
720
742
|
if (err) {
|
|
721
743
|
return renderErrorPage(
|
|
722
|
-
err.error,
|
|
723
|
-
|
|
744
|
+
err.error,
|
|
745
|
+
err.status,
|
|
746
|
+
segments,
|
|
747
|
+
[] as LayoutEntry[],
|
|
748
|
+
_req,
|
|
749
|
+
match,
|
|
750
|
+
responseHeaders,
|
|
751
|
+
clientBootstrap
|
|
724
752
|
);
|
|
725
753
|
}
|
|
726
754
|
// No captured signal — return bare 500
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
* See design/02-rendering-pipeline.md §"Parallel Slots"
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import type { ManifestSegmentNode } from './route-matcher.js';
|
|
20
|
-
import type { RouteMatch, InterceptionContext } from './pipeline.js';
|
|
21
|
-
import { SlotAccessGate } from './access-gate.js';
|
|
22
|
-
import { wrapSegmentWithErrorBoundaries } from './error-boundary-wrapper.js';
|
|
23
19
|
import { TimberErrorBoundary } from '#/client/error-boundary.js';
|
|
24
20
|
import SlotErrorFallback from '#/client/slot-error-fallback.js';
|
|
21
|
+
import { SlotAccessGate } from './access-gate.js';
|
|
22
|
+
import { wrapSegmentWithErrorBoundaries } from './error-boundary-wrapper.js';
|
|
23
|
+
import type { InterceptionContext, RouteMatch } from './pipeline.js';
|
|
24
|
+
import { DenySignal } from './primitives.js';
|
|
25
|
+
import type { ManifestSegmentNode } from './route-matcher.js';
|
|
25
26
|
|
|
26
27
|
type CreateElementFn = (...args: unknown[]) => React.ReactElement;
|
|
27
28
|
|
|
@@ -56,7 +57,39 @@ export async function resolveSlotElement(
|
|
|
56
57
|
const mod = (await slotMatch.page.load()) as Record<string, unknown>;
|
|
57
58
|
if (mod.default) {
|
|
58
59
|
const SlotPage = mod.default as (...args: unknown[]) => unknown;
|
|
59
|
-
|
|
60
|
+
|
|
61
|
+
// Load default.tsx fallback for notFound() handling in the slot page.
|
|
62
|
+
// When a slot page calls notFound() or deny(), it should gracefully
|
|
63
|
+
// degrade to default.tsx or null — not crash the page. This matches
|
|
64
|
+
// Next.js behavior. See design/02-rendering-pipeline.md
|
|
65
|
+
// §"Slot Access Failure = Graceful Degradation"
|
|
66
|
+
let denyFallback: React.ReactElement | null = null;
|
|
67
|
+
if (slotNode.default) {
|
|
68
|
+
const defaultMod = (await slotNode.default.load()) as Record<string, unknown>;
|
|
69
|
+
const DefaultComp = defaultMod.default as ((...args: unknown[]) => unknown) | undefined;
|
|
70
|
+
if (DefaultComp) {
|
|
71
|
+
denyFallback = h(DefaultComp, { params: paramsPromise, searchParams: {} });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Wrap the slot page to catch DenySignal (from notFound() or deny())
|
|
76
|
+
// at the component level. This prevents the signal from reaching the
|
|
77
|
+
// RSC onError callback and being tracked as a page-level denial, which
|
|
78
|
+
// would cause the pipeline to replace the entire successful SSR response
|
|
79
|
+
// with a deny page. Instead, the slot gracefully degrades.
|
|
80
|
+
const denyFallbackCapture = denyFallback;
|
|
81
|
+
const SafeSlotPage = async (props: Record<string, unknown>) => {
|
|
82
|
+
try {
|
|
83
|
+
return await (SlotPage as (props: Record<string, unknown>) => unknown)(props);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (error instanceof DenySignal) {
|
|
86
|
+
return denyFallbackCapture;
|
|
87
|
+
}
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
let element: React.ReactElement = h(SafeSlotPage, {
|
|
60
93
|
params: paramsPromise,
|
|
61
94
|
searchParams: {},
|
|
62
95
|
});
|
package/src/server/ssr-entry.ts
CHANGED
|
@@ -156,7 +156,10 @@ export async function handleSsr(
|
|
|
156
156
|
// Wrap in SsrStreamError so the RSC entry can handle it without
|
|
157
157
|
// re-executing server components via renderDenyPage.
|
|
158
158
|
// See LOCAL-293.
|
|
159
|
-
console.error(
|
|
159
|
+
console.error(
|
|
160
|
+
'[timber] SSR shell failed from RSC stream error:',
|
|
161
|
+
formatSsrError(renderError)
|
|
162
|
+
);
|
|
160
163
|
throw new SsrStreamError(
|
|
161
164
|
'SSR renderToReadableStream failed due to RSC stream error',
|
|
162
165
|
renderError
|
|
@@ -79,7 +79,10 @@ export interface AccessGateProps {
|
|
|
79
79
|
* - 'pass': render children
|
|
80
80
|
* - DenySignal/RedirectSignal: throw synchronously
|
|
81
81
|
*/
|
|
82
|
-
verdict?:
|
|
82
|
+
verdict?:
|
|
83
|
+
| 'pass'
|
|
84
|
+
| import('./primitives.js').DenySignal
|
|
85
|
+
| import('./primitives.js').RedirectSignal;
|
|
83
86
|
children: ReactElement;
|
|
84
87
|
}
|
|
85
88
|
|