@rangojs/router 0.0.0-experimental.eb0645d3 → 0.0.0-experimental.f1468e3c
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/AGENTS.md +8 -0
- package/README.md +126 -16
- package/dist/bin/rango.js +319 -95
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +2724 -1053
- package/package.json +68 -14
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +224 -32
- package/skills/caching/SKILL.md +279 -17
- package/skills/composability/SKILL.md +27 -3
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +11 -9
- package/skills/hooks/SKILL.md +243 -29
- package/skills/host-router/SKILL.md +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +68 -19
- package/skills/layout/SKILL.md +13 -9
- package/skills/links/SKILL.md +190 -23
- package/skills/loader/SKILL.md +235 -9
- package/skills/middleware/SKILL.md +18 -10
- package/skills/migrate-nextjs/SKILL.md +43 -19
- package/skills/migrate-react-router/SKILL.md +8 -2
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +18 -7
- package/skills/prerender/SKILL.md +65 -60
- package/skills/rango/SKILL.md +251 -24
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +115 -48
- package/skills/route/SKILL.md +46 -5
- package/skills/router-setup/SKILL.md +30 -8
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +130 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/typesafety/SKILL.md +322 -29
- package/skills/use-cache/SKILL.md +57 -14
- package/skills/view-transitions/SKILL.md +337 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +192 -150
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +94 -25
- package/src/browser/navigation-client.ts +121 -84
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +115 -67
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +147 -128
- package/src/browser/prefetch/cache.ts +107 -56
- package/src/browser/prefetch/fetch.ts +204 -34
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +30 -7
- package/src/browser/react/NavigationProvider.tsx +283 -118
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/deferred-handle-resolution.ts +75 -0
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +17 -14
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +10 -5
- package/src/browser/react/use-params.ts +11 -11
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +25 -3
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +99 -8
- package/src/browser/rsc-router.tsx +91 -24
- package/src/browser/scroll-restoration.ts +30 -17
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +214 -55
- package/src/browser/types.ts +80 -9
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +60 -35
- package/src/build/generate-route-types.ts +2 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +117 -14
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +117 -23
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +55 -28
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +224 -41
- package/src/cache/cache-scope.ts +188 -82
- package/src/cache/cache-tag.ts +103 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +1989 -378
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +89 -21
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +10 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +44 -21
- package/src/client.tsx +36 -61
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +18 -6
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +31 -23
- package/src/handles/MetaTags.tsx +62 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +96 -12
- package/src/index.ts +94 -14
- package/src/internal-debug.ts +11 -10
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +32 -37
- package/src/prerender.ts +61 -6
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +34 -0
- package/src/reverse.ts +65 -40
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +19 -77
- package/src/route-definition/dsl-helpers.ts +304 -309
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +82 -55
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/redirect.ts +44 -11
- package/src/route-definition/resolve-handler-use.ts +12 -1
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +0 -16
- package/src/route-types.ts +19 -46
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +73 -25
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +44 -23
- package/src/router/handler-context.ts +27 -43
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +39 -20
- package/src/router/lazy-includes.ts +10 -47
- package/src/router/loader-resolution.ts +155 -72
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +18 -29
- package/src/router/match-api.ts +9 -24
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +159 -285
- package/src/router/match-middleware/cache-store.ts +64 -52
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +44 -74
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -134
- package/src/router/middleware.ts +247 -166
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +85 -94
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +104 -65
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +28 -62
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +0 -1
- package/src/router/router-context.ts +3 -28
- package/src/router/router-interfaces.ts +83 -35
- package/src/router/router-options.ts +136 -5
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +97 -84
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +76 -39
- package/src/router/segment-resolution/revalidation.ts +272 -320
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +56 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +162 -64
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +110 -55
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +264 -220
- package/src/rsc/helpers.ts +100 -6
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +114 -38
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +117 -11
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +88 -188
- package/src/rsc/rsc-rendering.ts +98 -76
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +281 -117
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +23 -5
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-loader-promise.ts +31 -4
- package/src/segment-system.tsx +254 -143
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +163 -51
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +21 -38
- package/src/server/loader-registry.ts +33 -42
- package/src/server/request-context.ts +287 -178
- package/src/ssr/index.tsx +21 -16
- package/src/static-handler.ts +10 -13
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +701 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +257 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +371 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +357 -0
- package/src/testing/render-route.tsx +581 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +13 -4
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +110 -62
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +135 -14
- package/src/urls/include-helper.ts +9 -56
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +29 -12
- package/src/urls/path-helper.ts +17 -106
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +292 -107
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +8 -7
- package/src/vite/discovery/discover-routers.ts +126 -85
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +96 -68
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +44 -0
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +2 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +126 -8
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -21
- package/src/vite/plugins/expose-action-id.ts +48 -95
- package/src/vite/plugins/expose-id-utils.ts +88 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +11 -90
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +505 -486
- package/src/vite/plugins/performance-tracks.ts +26 -25
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +39 -25
- package/src/vite/rango.ts +109 -118
- package/src/vite/router-discovery.ts +718 -119
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +54 -39
- package/src/vite/utils/shared-utils.ts +90 -41
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -11,12 +11,18 @@ import {
|
|
|
11
11
|
getContext,
|
|
12
12
|
getNamePrefix,
|
|
13
13
|
getUrlPrefix,
|
|
14
|
+
requireDslContext,
|
|
14
15
|
type EntryData,
|
|
16
|
+
type EntryPropDatas,
|
|
17
|
+
type EntryPropSegments,
|
|
18
|
+
type HelperContext,
|
|
15
19
|
type InterceptEntry,
|
|
20
|
+
type InterceptConfig,
|
|
16
21
|
} from "../server/context";
|
|
17
22
|
import { invariant } from "../errors";
|
|
23
|
+
import { validateUserRouteName } from "../route-name.js";
|
|
18
24
|
import { isCachedFunction } from "../cache/taint.js";
|
|
19
|
-
import {
|
|
25
|
+
import { RangoContext } from "../server/context";
|
|
20
26
|
import { isStaticHandler } from "../static-handler.js";
|
|
21
27
|
import RootLayout from "../server/root-layout";
|
|
22
28
|
import type {
|
|
@@ -31,13 +37,13 @@ import type {
|
|
|
31
37
|
ErrorBoundaryItem,
|
|
32
38
|
NotFoundBoundaryItem,
|
|
33
39
|
LayoutItem,
|
|
34
|
-
WhenItem,
|
|
35
40
|
CacheItem,
|
|
36
41
|
TransitionItem,
|
|
37
42
|
UseItems,
|
|
38
43
|
} from "../route-types.js";
|
|
39
44
|
import type { RouteHelpers } from "./helpers-types.js";
|
|
40
45
|
import { resolveHandlerUse, mergeHandlerUse } from "./resolve-handler-use.js";
|
|
46
|
+
import { ALL_USE_ITEM_TYPES } from "./use-item-types.js";
|
|
41
47
|
|
|
42
48
|
/**
|
|
43
49
|
* Check if an item contains routes (directly or inside nested structures like cache).
|
|
@@ -61,16 +67,105 @@ const hasRoutesInItem = (item: AllUseItems): boolean => {
|
|
|
61
67
|
return false;
|
|
62
68
|
};
|
|
63
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Fresh empty collections shared by every from-scratch segment entry. Returns
|
|
72
|
+
* new arrays/objects per call so no two entries share mutable references.
|
|
73
|
+
* mountPath is intentionally NOT included here — each call site adds it from
|
|
74
|
+
* getUrlPrefix() where applicable: the route() and transition() helpers add
|
|
75
|
+
* none, while path() (which also builds a `type: "route"` entry) and the
|
|
76
|
+
* structural helpers (layout/cache/middleware/parallel) do.
|
|
77
|
+
*/
|
|
78
|
+
const emptySegmentBase = (): EntryPropDatas &
|
|
79
|
+
EntryPropSegments & { loading: undefined } => ({
|
|
80
|
+
loading: undefined,
|
|
81
|
+
middleware: [],
|
|
82
|
+
revalidate: [],
|
|
83
|
+
errorBoundary: [],
|
|
84
|
+
notFoundBoundary: [],
|
|
85
|
+
layout: [],
|
|
86
|
+
parallel: {},
|
|
87
|
+
intercept: [],
|
|
88
|
+
loader: [],
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Run a children/use callback as a nested scope, flatten the result, and assert
|
|
93
|
+
* every item is a valid use item. `kind` preserves the existing error wording
|
|
94
|
+
* ("use()" vs "children" callback).
|
|
95
|
+
*/
|
|
96
|
+
function runAndValidateUseItems(
|
|
97
|
+
store: ReturnType<typeof getContext>,
|
|
98
|
+
namespace: string,
|
|
99
|
+
entry: EntryData,
|
|
100
|
+
cb: () => any,
|
|
101
|
+
label: string,
|
|
102
|
+
kind: "use" | "children",
|
|
103
|
+
): AllUseItems[] {
|
|
104
|
+
const result = store.run(namespace, entry, cb)?.flat(3);
|
|
105
|
+
return validateUseItems(result, namespace, label, kind);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Assert an already-invoked, flattened callback result is a use-item array. */
|
|
109
|
+
function validateUseItems(
|
|
110
|
+
result: any,
|
|
111
|
+
namespace: string,
|
|
112
|
+
label: string,
|
|
113
|
+
kind: "use" | "children",
|
|
114
|
+
): AllUseItems[] {
|
|
115
|
+
invariant(
|
|
116
|
+
Array.isArray(result) && result.every((item) => isValidUseItem(item)),
|
|
117
|
+
`${label}() ${kind === "use" ? "use()" : "children"} callback must return an array of use items [${namespace}]`,
|
|
118
|
+
);
|
|
119
|
+
return result as AllUseItems[];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** True when a children/use result contains no routes (directly or nested). */
|
|
123
|
+
const isOrphan = (result: AllUseItems[]): boolean =>
|
|
124
|
+
!result.some((item) => item != null && hasRoutesInItem(item));
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Register a routeless structural entry as an orphan sibling: clear its parent
|
|
128
|
+
* pointer so it leaves the middleware/parent-pointer chain (LOAD-BEARING — see
|
|
129
|
+
* docs/tree-structure.md) and push it onto the parent's layout[] so it renders
|
|
130
|
+
* as a wrapper. Used by cache()/middleware()/transition(); layout() runs extra
|
|
131
|
+
* validation and registers inline.
|
|
132
|
+
*/
|
|
133
|
+
const attachOrphanSibling = (
|
|
134
|
+
parent: EntryData | null,
|
|
135
|
+
entry: EntryData,
|
|
136
|
+
): void => {
|
|
137
|
+
entry.parent = null;
|
|
138
|
+
if (parent && "layout" in parent) parent.layout.push(entry);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Run `fn` with `ctx.parent` temporarily redirected to `temp` — a satellite
|
|
143
|
+
* entry that captures the attachments declared by a use() callback — restoring
|
|
144
|
+
* the original parent afterward, including on throw. loader()/intercept() each
|
|
145
|
+
* build their own tempParent shape (intercept keeps a loading get/set accessor
|
|
146
|
+
* and a captured-layouts array); this only centralizes the save/restore.
|
|
147
|
+
*/
|
|
148
|
+
function withParent<T>(ctx: HelperContext, temp: EntryData, fn: () => T): T {
|
|
149
|
+
const original = ctx.parent;
|
|
150
|
+
ctx.parent = temp;
|
|
151
|
+
try {
|
|
152
|
+
return fn();
|
|
153
|
+
} finally {
|
|
154
|
+
ctx.parent = original;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
64
158
|
const revalidate: RouteHelpers<any, any>["revalidate"] = (fn) => {
|
|
65
|
-
const ctx =
|
|
66
|
-
|
|
159
|
+
const { store, ctx } = requireDslContext(
|
|
160
|
+
"revalidate() must be called inside urls()",
|
|
161
|
+
);
|
|
67
162
|
|
|
68
163
|
// Attach to last entry in stack
|
|
69
164
|
const parent = ctx.parent;
|
|
70
165
|
if (!parent || !("revalidate" in parent)) {
|
|
71
166
|
invariant(false, "No parent entry available for revalidate()");
|
|
72
167
|
}
|
|
73
|
-
const name = `$${
|
|
168
|
+
const name = `$${store.getNextIndex("revalidate")}`;
|
|
74
169
|
parent.revalidate.push(fn);
|
|
75
170
|
return { name, type: "revalidate" } as RevalidateItem;
|
|
76
171
|
};
|
|
@@ -108,15 +203,16 @@ const revalidate: RouteHelpers<any, any>["revalidate"] = (fn) => {
|
|
|
108
203
|
* ```
|
|
109
204
|
*/
|
|
110
205
|
const errorBoundary: RouteHelpers<any, any>["errorBoundary"] = (fallback) => {
|
|
111
|
-
const ctx =
|
|
112
|
-
|
|
206
|
+
const { store, ctx } = requireDslContext(
|
|
207
|
+
"errorBoundary() must be called inside urls()",
|
|
208
|
+
);
|
|
113
209
|
|
|
114
210
|
// Attach to parent entry in stack
|
|
115
211
|
const parent = ctx.parent;
|
|
116
212
|
if (!parent || !("errorBoundary" in parent)) {
|
|
117
213
|
invariant(false, "No parent entry available for errorBoundary()");
|
|
118
214
|
}
|
|
119
|
-
const name = `$${
|
|
215
|
+
const name = `$${store.getNextIndex("errorBoundary")}`;
|
|
120
216
|
parent.errorBoundary.push(fallback);
|
|
121
217
|
return { name, type: "errorBoundary" } as ErrorBoundaryItem;
|
|
122
218
|
};
|
|
@@ -155,46 +251,20 @@ const errorBoundary: RouteHelpers<any, any>["errorBoundary"] = (fallback) => {
|
|
|
155
251
|
const notFoundBoundary: RouteHelpers<any, any>["notFoundBoundary"] = (
|
|
156
252
|
fallback,
|
|
157
253
|
) => {
|
|
158
|
-
const ctx =
|
|
159
|
-
|
|
254
|
+
const { store, ctx } = requireDslContext(
|
|
255
|
+
"notFoundBoundary() must be called inside urls()",
|
|
256
|
+
);
|
|
160
257
|
|
|
161
258
|
// Attach to parent entry in stack
|
|
162
259
|
const parent = ctx.parent;
|
|
163
260
|
if (!parent || !("notFoundBoundary" in parent)) {
|
|
164
261
|
invariant(false, "No parent entry available for notFoundBoundary()");
|
|
165
262
|
}
|
|
166
|
-
const name = `$${
|
|
263
|
+
const name = `$${store.getNextIndex("notFoundBoundary")}`;
|
|
167
264
|
parent.notFoundBoundary.push(fallback);
|
|
168
265
|
return { name, type: "notFoundBoundary" } as NotFoundBoundaryItem;
|
|
169
266
|
};
|
|
170
267
|
|
|
171
|
-
/**
|
|
172
|
-
* When helper - defines a condition for intercept activation
|
|
173
|
-
*
|
|
174
|
-
* Only valid inside intercept() use() callback. The when() function
|
|
175
|
-
* is captured by the intercept and stored in its `when` array.
|
|
176
|
-
* During soft navigation, all when() conditions must return true
|
|
177
|
-
* for the intercept to activate.
|
|
178
|
-
*/
|
|
179
|
-
const when: RouteHelpers<any, any>["when"] = (fn) => {
|
|
180
|
-
const ctx = getContext().getStore();
|
|
181
|
-
if (!ctx) throw new Error("when() must be called inside intercept()");
|
|
182
|
-
|
|
183
|
-
// The when() function needs to be captured by the intercept's tempParent
|
|
184
|
-
// which should have a `when` array. If not present, we're not inside intercept()
|
|
185
|
-
const parent = ctx.parent as any;
|
|
186
|
-
if (!parent || !("when" in parent)) {
|
|
187
|
-
invariant(
|
|
188
|
-
false,
|
|
189
|
-
"when() can only be used inside intercept() use() callback",
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const name = `$${getContext().getNextIndex("when")}`;
|
|
194
|
-
parent.when.push(fn);
|
|
195
|
-
return { name, type: "when" } as WhenItem;
|
|
196
|
-
};
|
|
197
|
-
|
|
198
268
|
/**
|
|
199
269
|
* Cache helper - defines caching configuration for segments
|
|
200
270
|
*
|
|
@@ -205,21 +275,21 @@ const when: RouteHelpers<any, any>["when"] = (fn) => {
|
|
|
205
275
|
* Supports these call signatures:
|
|
206
276
|
* - cache() - no args, uses app-level defaults (for loader caching)
|
|
207
277
|
* - cache(() => [...]) - wraps children with app-level defaults
|
|
208
|
-
* - cache('profileName') - uses a named cache profile
|
|
209
|
-
* - cache('profileName', () => [...]) - named profile with children
|
|
210
278
|
* - cache({ ttl: 60 }, () => [...]) - with explicit options
|
|
279
|
+
*
|
|
280
|
+
* Named cache profiles are applied via the `"use cache: <profile>"` directive,
|
|
281
|
+
* not a `cache("profileName")` form in the route tree.
|
|
211
282
|
*/
|
|
212
283
|
const cache: RouteHelpers<any, any>["cache"] = (
|
|
213
284
|
optionsOrChildren?:
|
|
214
285
|
| PartialCacheOptions
|
|
215
286
|
| false
|
|
216
|
-
| string
|
|
217
287
|
| (() => UseItems<AllUseItems>),
|
|
218
288
|
maybeChildren?: () => UseItems<AllUseItems>,
|
|
219
289
|
) => {
|
|
220
|
-
const store =
|
|
221
|
-
|
|
222
|
-
|
|
290
|
+
const { store, ctx } = requireDslContext(
|
|
291
|
+
"cache() must be called inside urls()",
|
|
292
|
+
);
|
|
223
293
|
|
|
224
294
|
// Handle overloaded signature
|
|
225
295
|
let options: PartialCacheOptions | false;
|
|
@@ -229,18 +299,6 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
229
299
|
// cache() - no args, use defaults
|
|
230
300
|
options = {};
|
|
231
301
|
children = undefined;
|
|
232
|
-
} else if (typeof optionsOrChildren === "string") {
|
|
233
|
-
// cache('profileName') or cache('profileName', () => [...])
|
|
234
|
-
// Resolve from context-scoped profiles (set per-router via HelperContext).
|
|
235
|
-
const ctxStore = RSCRouterContext.getStore();
|
|
236
|
-
const profile = ctxStore?.cacheProfiles?.[optionsOrChildren];
|
|
237
|
-
invariant(
|
|
238
|
-
profile,
|
|
239
|
-
`cache("${optionsOrChildren}"): unknown cache profile. ` +
|
|
240
|
-
`Define it in createRouter({ cacheProfiles: { "${optionsOrChildren}": { ttl: ... } } }).`,
|
|
241
|
-
);
|
|
242
|
-
options = { ttl: profile.ttl, swr: profile.swr, tags: profile.tags };
|
|
243
|
-
children = maybeChildren;
|
|
244
302
|
} else if (typeof optionsOrChildren === "function") {
|
|
245
303
|
// cache(() => [...]) - use empty options (will use defaults)
|
|
246
304
|
options = {};
|
|
@@ -271,26 +329,18 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
271
329
|
// Create orphan cache entry (like orphan layout)
|
|
272
330
|
// Subsequent siblings in the same array will attach to this entry
|
|
273
331
|
const namespace = `${ctx.namespace}.${cacheIndex}`;
|
|
274
|
-
const
|
|
332
|
+
const urlPrefix = getUrlPrefix();
|
|
275
333
|
|
|
276
334
|
const entry = {
|
|
335
|
+
...emptySegmentBase(),
|
|
277
336
|
id: namespace,
|
|
278
337
|
shortCode: store.getShortCode("cache"),
|
|
279
338
|
type: "cache",
|
|
280
339
|
parent: parent, // link to current parent for hierarchy
|
|
281
340
|
cache: cacheConfig,
|
|
282
341
|
handler: RootLayout,
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
revalidate: [],
|
|
286
|
-
errorBoundary: [],
|
|
287
|
-
notFoundBoundary: [],
|
|
288
|
-
layout: [],
|
|
289
|
-
parallel: {},
|
|
290
|
-
intercept: [],
|
|
291
|
-
loader: [],
|
|
292
|
-
...(cacheUrlPrefix ? { mountPath: cacheUrlPrefix } : {}),
|
|
293
|
-
} as EntryData;
|
|
342
|
+
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
343
|
+
} satisfies EntryData;
|
|
294
344
|
|
|
295
345
|
// Attach to parent's layout array (cache entries are structural like layouts)
|
|
296
346
|
if (parent && "layout" in parent) {
|
|
@@ -304,13 +354,23 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
304
354
|
return { name: namespace, type: "cache" } as CacheItem;
|
|
305
355
|
}
|
|
306
356
|
|
|
357
|
+
// Inside a loader() use() callback, only the direct form — cache()/cache(opts)
|
|
358
|
+
// — writes cache config to the loader entry. The wrapper form creates a
|
|
359
|
+
// structural cache boundary with its own children scope, which has no effect
|
|
360
|
+
// on the loader and would silently no-op.
|
|
361
|
+
invariant(
|
|
362
|
+
!(ctx.parent && (ctx.parent as any).type === "loader"),
|
|
363
|
+
"cache() wrapper form is not valid inside loader() use(). Use cache({...}) without children to configure the loader's cache.",
|
|
364
|
+
);
|
|
365
|
+
|
|
307
366
|
// With children: create a cache entry (like layout with caching semantics)
|
|
308
367
|
const namespace = `${ctx.namespace}.${cacheIndex}`;
|
|
309
368
|
const cacheShortCode = store.getShortCode("cache");
|
|
310
369
|
|
|
311
|
-
const
|
|
370
|
+
const urlPrefix = getUrlPrefix();
|
|
312
371
|
|
|
313
372
|
const entry = {
|
|
373
|
+
...emptySegmentBase(),
|
|
314
374
|
id: namespace,
|
|
315
375
|
shortCode: cacheShortCode,
|
|
316
376
|
type: "cache",
|
|
@@ -318,40 +378,22 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
318
378
|
cache: cacheConfig,
|
|
319
379
|
// Cache entries render like layouts (with Outlet as default handler)
|
|
320
380
|
handler: RootLayout, // RootLayout just renders <Outlet />
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
revalidate: [],
|
|
324
|
-
errorBoundary: [],
|
|
325
|
-
notFoundBoundary: [],
|
|
326
|
-
layout: [],
|
|
327
|
-
parallel: {},
|
|
328
|
-
intercept: [],
|
|
329
|
-
loader: [],
|
|
330
|
-
...(cacheUrlPrefix2 ? { mountPath: cacheUrlPrefix2 } : {}),
|
|
331
|
-
} as EntryData;
|
|
381
|
+
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
382
|
+
} satisfies EntryData;
|
|
332
383
|
|
|
333
384
|
// Run children with cache entry as parent
|
|
334
|
-
const result =
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
385
|
+
const result = runAndValidateUseItems(
|
|
386
|
+
store,
|
|
387
|
+
namespace,
|
|
388
|
+
entry,
|
|
389
|
+
children,
|
|
390
|
+
"cache",
|
|
391
|
+
"children",
|
|
339
392
|
);
|
|
340
393
|
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
Array.isArray(result) &&
|
|
345
|
-
result.some((item) => hasRoutesInItem(item));
|
|
346
|
-
|
|
347
|
-
if (!hasRoutes) {
|
|
348
|
-
const parent = ctx.parent;
|
|
349
|
-
if (parent && "layout" in parent) {
|
|
350
|
-
// Attach to parent's layout array (cache entries are structural like layouts)
|
|
351
|
-
entry.parent = null;
|
|
352
|
-
parent.layout.push(entry);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
394
|
+
// Cache entries are structural like layouts: with no routes inside, register
|
|
395
|
+
// as an orphan sibling.
|
|
396
|
+
if (isOrphan(result)) attachOrphanSibling(ctx.parent, entry);
|
|
355
397
|
|
|
356
398
|
return { name: namespace, type: "cache", uses: result } as CacheItem;
|
|
357
399
|
};
|
|
@@ -397,9 +439,9 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
397
439
|
}
|
|
398
440
|
}
|
|
399
441
|
|
|
400
|
-
const store =
|
|
401
|
-
|
|
402
|
-
|
|
442
|
+
const { store, ctx } = requireDslContext(
|
|
443
|
+
"middleware() must be called inside urls()",
|
|
444
|
+
);
|
|
403
445
|
|
|
404
446
|
if (!children) {
|
|
405
447
|
// Sibling mode: attach to parent entry
|
|
@@ -418,22 +460,15 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
418
460
|
|
|
419
461
|
const urlPrefix = getUrlPrefix();
|
|
420
462
|
const entry = {
|
|
463
|
+
...emptySegmentBase(),
|
|
421
464
|
id: namespace,
|
|
422
465
|
shortCode: store.getShortCode("layout"),
|
|
423
466
|
type: "layout",
|
|
424
467
|
parent: ctx.parent,
|
|
425
468
|
handler: RootLayout,
|
|
426
|
-
loading: undefined,
|
|
427
469
|
middleware: [...fns],
|
|
428
|
-
revalidate: [],
|
|
429
|
-
errorBoundary: [],
|
|
430
|
-
notFoundBoundary: [],
|
|
431
|
-
layout: [],
|
|
432
|
-
parallel: {},
|
|
433
|
-
intercept: [],
|
|
434
|
-
loader: [],
|
|
435
470
|
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
436
|
-
}
|
|
471
|
+
} satisfies EntryData;
|
|
437
472
|
|
|
438
473
|
// Run children callback. If the second arg was actually a middleware fn
|
|
439
474
|
// (old variadic form: middleware(mw1, mw2)), this will return a non-array
|
|
@@ -446,25 +481,14 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
446
481
|
"To pass multiple middleware, use middleware([fn1, fn2]).",
|
|
447
482
|
);
|
|
448
483
|
|
|
449
|
-
const result =
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
484
|
+
const result = validateUseItems(
|
|
485
|
+
rawResult.flat(3),
|
|
486
|
+
namespace,
|
|
487
|
+
"middleware",
|
|
488
|
+
"children",
|
|
454
489
|
);
|
|
455
490
|
|
|
456
|
-
|
|
457
|
-
result &&
|
|
458
|
-
Array.isArray(result) &&
|
|
459
|
-
result.some((item) => item != null && hasRoutesInItem(item));
|
|
460
|
-
|
|
461
|
-
if (!hasRoutes) {
|
|
462
|
-
const parent = ctx.parent;
|
|
463
|
-
if (parent && "layout" in parent) {
|
|
464
|
-
entry.parent = null;
|
|
465
|
-
parent.layout.push(entry);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
491
|
+
if (isOrphan(result)) attachOrphanSibling(ctx.parent, entry);
|
|
468
492
|
|
|
469
493
|
return {
|
|
470
494
|
name: namespace,
|
|
@@ -473,10 +497,26 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
473
497
|
} as MiddlewareItem;
|
|
474
498
|
};
|
|
475
499
|
|
|
500
|
+
// Slot names become part of segment ids: a parallel/intercept slot is encoded
|
|
501
|
+
// as `${shortCode}.${slotName}`, and loader segments append `D${index}.${loaderId}`.
|
|
502
|
+
// A "." in the slot name collides with that separator -- loaderParentId
|
|
503
|
+
// (segment-system.tsx) strips from the FIRST `D<index>.`, so a name like
|
|
504
|
+
// "@D3.foo" is mis-cut to "@" and the loader's data is silently dropped. Reject
|
|
505
|
+
// the dot at definition time so the failure is loud, not a corrupted tree at
|
|
506
|
+
// runtime. (A bare "D" without a trailing dot -- e.g. "@Detail" -- is fine.)
|
|
507
|
+
function assertValidSlotName(slotName: string): void {
|
|
508
|
+
invariant(
|
|
509
|
+
!slotName.includes("."),
|
|
510
|
+
`Slot name "${slotName}" must not contain ".". The dot is a reserved ` +
|
|
511
|
+
`segment-id separator; a name like "@D3.foo" corrupts loader segment-id ` +
|
|
512
|
+
`parsing and silently drops the loader's data. Rename the slot.`,
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
|
|
476
516
|
const parallel: RouteHelpers<any, any>["parallel"] = (slots, use) => {
|
|
477
|
-
const store =
|
|
478
|
-
|
|
479
|
-
|
|
517
|
+
const { store, ctx } = requireDslContext(
|
|
518
|
+
"parallel() must be called inside urls()",
|
|
519
|
+
);
|
|
480
520
|
|
|
481
521
|
if (!ctx.parent || !ctx.parent?.parallel) {
|
|
482
522
|
invariant(false, "No parent entry available for parallel()");
|
|
@@ -488,6 +528,7 @@ const parallel: RouteHelpers<any, any>["parallel"] = (slots, use) => {
|
|
|
488
528
|
);
|
|
489
529
|
|
|
490
530
|
const slotNames = Object.keys(slots as Record<string, any>) as `@${string}`[];
|
|
531
|
+
for (const slotName of slotNames) assertValidSlotName(slotName);
|
|
491
532
|
|
|
492
533
|
const namespace = `${ctx.namespace}.$${store.getNextIndex("parallel")}`;
|
|
493
534
|
|
|
@@ -528,20 +569,12 @@ const parallel: RouteHelpers<any, any>["parallel"] = (slots, use) => {
|
|
|
528
569
|
// Create full EntryData for parallel with its own loaders/revalidate/loading
|
|
529
570
|
const parallelUrlPrefix = getUrlPrefix();
|
|
530
571
|
const entry = {
|
|
572
|
+
...emptySegmentBase(),
|
|
531
573
|
id: namespace,
|
|
532
574
|
shortCode: store.getShortCode("parallel"),
|
|
533
575
|
type: "parallel",
|
|
534
576
|
parent: null, // Parallels don't participate in parent chain traversal
|
|
535
577
|
handler: unwrappedSlots,
|
|
536
|
-
loading: undefined, // Allow loading() to attach loading state
|
|
537
|
-
middleware: [],
|
|
538
|
-
revalidate: [],
|
|
539
|
-
errorBoundary: [],
|
|
540
|
-
notFoundBoundary: [],
|
|
541
|
-
layout: [],
|
|
542
|
-
parallel: {},
|
|
543
|
-
intercept: [],
|
|
544
|
-
loader: [],
|
|
545
578
|
...(parallelUrlPrefix ? { mountPath: parallelUrlPrefix } : {}),
|
|
546
579
|
...(hasStaticSlot
|
|
547
580
|
? {
|
|
@@ -596,10 +629,13 @@ const parallel: RouteHelpers<any, any>["parallel"] = (slots, use) => {
|
|
|
596
629
|
"parallel",
|
|
597
630
|
);
|
|
598
631
|
if (slotMergedUse) {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
632
|
+
runAndValidateUseItems(
|
|
633
|
+
store,
|
|
634
|
+
namespace,
|
|
635
|
+
slotEntry,
|
|
636
|
+
slotMergedUse,
|
|
637
|
+
"parallel",
|
|
638
|
+
"use",
|
|
603
639
|
);
|
|
604
640
|
}
|
|
605
641
|
|
|
@@ -637,11 +673,22 @@ const intercept = (
|
|
|
637
673
|
slotName: `@${string}`,
|
|
638
674
|
routeName: string,
|
|
639
675
|
handler: any,
|
|
676
|
+
configOrUse?: InterceptConfig | (() => any[]),
|
|
640
677
|
use?: () => any[],
|
|
641
678
|
) => {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
679
|
+
// arg4 discrimination: a function is the use() callback (no config); an object
|
|
680
|
+
// is the config carrying `when`. With config given, the use() callback is
|
|
681
|
+
// arg5. Keeps the no-config form intercept(slot, route, handler, () => [...])
|
|
682
|
+
// working unchanged.
|
|
683
|
+
const config: InterceptConfig | undefined =
|
|
684
|
+
typeof configOrUse === "function" || configOrUse == null
|
|
685
|
+
? undefined
|
|
686
|
+
: configOrUse;
|
|
687
|
+
const useFn = typeof configOrUse === "function" ? configOrUse : use;
|
|
688
|
+
|
|
689
|
+
const { store, ctx } = requireDslContext(
|
|
690
|
+
"intercept() must be called inside urls()",
|
|
691
|
+
);
|
|
645
692
|
|
|
646
693
|
if (!ctx.parent || !ctx.parent?.intercept) {
|
|
647
694
|
invariant(false, "No parent entry available for intercept()");
|
|
@@ -652,6 +699,8 @@ const intercept = (
|
|
|
652
699
|
"intercept() cannot be used inside parallel()",
|
|
653
700
|
);
|
|
654
701
|
|
|
702
|
+
assertValidSlotName(slotName);
|
|
703
|
+
|
|
655
704
|
const namespace = `${ctx.namespace}.$${store.getNextIndex("intercept")}.${slotName}`;
|
|
656
705
|
|
|
657
706
|
// Dot-prefixed = local (add include prefix), unprefixed = global (use as-is)
|
|
@@ -674,29 +723,33 @@ const intercept = (
|
|
|
674
723
|
when: [], // Selector conditions for conditional interception
|
|
675
724
|
};
|
|
676
725
|
|
|
726
|
+
// Conditional interception: `when` from the config object — a single selector
|
|
727
|
+
// or an array (ALL must return true to activate). Replaces the former when()
|
|
728
|
+
// use-item captured inside the callback.
|
|
729
|
+
if (config?.when) {
|
|
730
|
+
const selectors = Array.isArray(config.when) ? config.when : [config.when];
|
|
731
|
+
entry.when.push(...selectors);
|
|
732
|
+
}
|
|
733
|
+
|
|
677
734
|
// Merge handler.use defaults with explicit use
|
|
678
735
|
const handlerUseFn = resolveHandlerUse(handler);
|
|
679
|
-
const mergedUse = mergeHandlerUse(handlerUseFn,
|
|
736
|
+
const mergedUse = mergeHandlerUse(handlerUseFn, useFn, "intercept");
|
|
680
737
|
|
|
681
738
|
// Run merged use callback to collect loaders, revalidate, middleware, etc.
|
|
682
739
|
if (mergedUse) {
|
|
683
|
-
//
|
|
684
|
-
// so that middleware, loader, revalidate attach to the intercept entry
|
|
685
|
-
const originalParent = ctx.parent;
|
|
686
|
-
|
|
687
|
-
// Capture layouts in a temporary array
|
|
740
|
+
// Capture layout() calls into a temporary array
|
|
688
741
|
const capturedLayouts: EntryData[] = [];
|
|
689
742
|
|
|
743
|
+
// Temporary parent so middleware/loader/revalidate/when attach to the
|
|
744
|
+
// intercept entry; the loading get/set accessor mirrors writes onto `entry`.
|
|
690
745
|
const tempParent = {
|
|
691
|
-
...
|
|
746
|
+
...ctx.parent,
|
|
692
747
|
middleware: entry.middleware,
|
|
693
748
|
revalidate: entry.revalidate,
|
|
694
749
|
errorBoundary: entry.errorBoundary,
|
|
695
750
|
notFoundBoundary: entry.notFoundBoundary,
|
|
696
751
|
loader: entry.loader,
|
|
697
752
|
layout: capturedLayouts, // Capture layout() calls
|
|
698
|
-
when: entry.when, // Capture when() conditions
|
|
699
|
-
// Use getter/setter to capture loading on the entry
|
|
700
753
|
get loading() {
|
|
701
754
|
return entry.loading;
|
|
702
755
|
},
|
|
@@ -704,12 +757,10 @@ const intercept = (
|
|
|
704
757
|
entry.loading = value;
|
|
705
758
|
},
|
|
706
759
|
};
|
|
707
|
-
ctx.parent = tempParent as EntryData;
|
|
708
760
|
|
|
709
|
-
const result =
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
ctx.parent = originalParent;
|
|
761
|
+
const result = withParent(ctx, tempParent as EntryData, () =>
|
|
762
|
+
mergedUse()?.flat(3),
|
|
763
|
+
);
|
|
713
764
|
|
|
714
765
|
// Extract layout from captured layouts (use first one if multiple)
|
|
715
766
|
// Layout inside intercept should always be ReactNode or Handler, not Record slots
|
|
@@ -719,10 +770,7 @@ const intercept = (
|
|
|
719
770
|
| Handler<any, any, any>;
|
|
720
771
|
}
|
|
721
772
|
|
|
722
|
-
|
|
723
|
-
Array.isArray(result) && result.every((item) => isValidUseItem(item)),
|
|
724
|
-
`intercept() use() callback must return an array of use items [${namespace}]`,
|
|
725
|
-
);
|
|
773
|
+
validateUseItems(result, namespace, "intercept", "use");
|
|
726
774
|
}
|
|
727
775
|
|
|
728
776
|
ctx.parent.intercept.push(entry);
|
|
@@ -732,10 +780,10 @@ const intercept = (
|
|
|
732
780
|
/**
|
|
733
781
|
* Loader helper - attaches a loader to the current entry
|
|
734
782
|
*/
|
|
735
|
-
const
|
|
736
|
-
const store =
|
|
737
|
-
|
|
738
|
-
|
|
783
|
+
const loader: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
784
|
+
const { store, ctx } = requireDslContext(
|
|
785
|
+
"loader() must be called inside urls()",
|
|
786
|
+
);
|
|
739
787
|
|
|
740
788
|
// Attach to last entry in stack
|
|
741
789
|
if (!ctx.parent || !ctx.parent?.loader) {
|
|
@@ -750,25 +798,28 @@ const loaderFn: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
|
750
798
|
revalidate: [] as ShouldRevalidateFn<any, any>[],
|
|
751
799
|
};
|
|
752
800
|
|
|
753
|
-
//
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
801
|
+
// Merge handler.use defaults (attached to the loader definition) with explicit use
|
|
802
|
+
const handlerUseFn = resolveHandlerUse(loaderDef);
|
|
803
|
+
const mergedUse = mergeHandlerUse(handlerUseFn, use, "loader");
|
|
804
|
+
|
|
805
|
+
// If any use callback is in effect, run it to collect revalidation rules and cache config
|
|
806
|
+
if (mergedUse) {
|
|
757
807
|
// Create a temporary "parent" with type "loader" so cache() can detect it.
|
|
758
808
|
// Save existing .cache to distinguish inherited config from newly set config.
|
|
759
|
-
const parentCache = (
|
|
809
|
+
const parentCache = (ctx.parent as any).cache;
|
|
760
810
|
const tempParent = {
|
|
761
|
-
...
|
|
811
|
+
...ctx.parent,
|
|
762
812
|
type: "loader",
|
|
763
813
|
revalidate: loaderEntry.revalidate,
|
|
764
814
|
};
|
|
765
|
-
ctx.parent = tempParent as EntryData;
|
|
766
815
|
|
|
767
|
-
const result =
|
|
816
|
+
const result = withParent(ctx, tempParent as EntryData, () =>
|
|
817
|
+
mergedUse()?.flat(3),
|
|
818
|
+
);
|
|
768
819
|
|
|
769
820
|
// Copy cache config only if cache() was called during the use() callback.
|
|
770
|
-
// The spread
|
|
771
|
-
//
|
|
821
|
+
// The spread may carry an inherited .cache from a parent cache() boundary —
|
|
822
|
+
// only copy if it was newly set.
|
|
772
823
|
if (
|
|
773
824
|
(tempParent as any).cache &&
|
|
774
825
|
(tempParent as any).cache !== parentCache
|
|
@@ -776,13 +827,7 @@ const loaderFn: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
|
776
827
|
(loaderEntry as any).cache = (tempParent as any).cache;
|
|
777
828
|
}
|
|
778
829
|
|
|
779
|
-
|
|
780
|
-
ctx.parent = originalParent;
|
|
781
|
-
|
|
782
|
-
invariant(
|
|
783
|
-
Array.isArray(result) && result.every((item) => isValidUseItem(item)),
|
|
784
|
-
`loader() use() callback must return an array of use items [${name}]`,
|
|
785
|
-
);
|
|
830
|
+
validateUseItems(result, name, "loader", "use");
|
|
786
831
|
}
|
|
787
832
|
|
|
788
833
|
ctx.parent.loader.push(loaderEntry);
|
|
@@ -793,10 +838,10 @@ const loaderFn: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
|
793
838
|
* Loading helper - attaches a loading component to the current entry
|
|
794
839
|
* Loading components are static (no context) and shown during navigation
|
|
795
840
|
*/
|
|
796
|
-
const
|
|
797
|
-
const store =
|
|
798
|
-
|
|
799
|
-
|
|
841
|
+
const loading: RouteHelpers<any, any>["loading"] = (component, options) => {
|
|
842
|
+
const { store, ctx } = requireDslContext(
|
|
843
|
+
"loading() must be called inside urls()",
|
|
844
|
+
);
|
|
800
845
|
|
|
801
846
|
const parent = ctx.parent;
|
|
802
847
|
if (!parent || !("loading" in parent)) {
|
|
@@ -819,10 +864,13 @@ const loadingFn: RouteHelpers<any, any>["loading"] = (component, options) => {
|
|
|
819
864
|
};
|
|
820
865
|
|
|
821
866
|
/**
|
|
822
|
-
* Transition helper -
|
|
823
|
-
*
|
|
867
|
+
* Transition helper - opts the entry (or a wrapped group of routes) into
|
|
868
|
+
* transition-driven navigation by attaching a TransitionConfig. This drives the
|
|
869
|
+
* commit through startTransition (content hold on all React versions) and, on
|
|
870
|
+
* experimental React, places a `<ViewTransition>` boundary unless
|
|
871
|
+
* `viewTransition: false`. See skills/view-transitions for the matrix.
|
|
824
872
|
*/
|
|
825
|
-
const
|
|
873
|
+
const transition = (
|
|
826
874
|
configOrChildren?: TransitionConfig | (() => UseItems<AllUseItems>),
|
|
827
875
|
maybeChildren?: () => UseItems<AllUseItems>,
|
|
828
876
|
): TransitionItem => {
|
|
@@ -836,11 +884,15 @@ const transitionFn = (
|
|
|
836
884
|
const children: (() => UseItems<AllUseItems>) | undefined =
|
|
837
885
|
typeof configOrChildren === "function" ? configOrChildren : maybeChildren;
|
|
838
886
|
|
|
839
|
-
const store =
|
|
840
|
-
|
|
841
|
-
|
|
887
|
+
const { store, ctx } = requireDslContext(
|
|
888
|
+
"transition() must be called inside urls()",
|
|
889
|
+
);
|
|
842
890
|
|
|
843
|
-
|
|
891
|
+
// Allocate a single index for this transition() call (used in all paths),
|
|
892
|
+
// mirroring cache() — the child form uses it for the name, the wrapper form
|
|
893
|
+
// reuses it for the namespace, so no index is burned.
|
|
894
|
+
const transitionIndex = store.getNextIndex("transition");
|
|
895
|
+
const name = `$${transitionIndex}`;
|
|
844
896
|
|
|
845
897
|
if (!children) {
|
|
846
898
|
// Position 1: child of path() — attach to parent entry
|
|
@@ -853,70 +905,51 @@ const transitionFn = (
|
|
|
853
905
|
}
|
|
854
906
|
|
|
855
907
|
// Position 2: wrapper — create a transparent layout with transition config
|
|
856
|
-
const namespace = `${ctx.namespace}.${
|
|
908
|
+
const namespace = `${ctx.namespace}.${transitionIndex}`;
|
|
857
909
|
const entry = {
|
|
910
|
+
...emptySegmentBase(),
|
|
858
911
|
id: namespace,
|
|
859
912
|
shortCode: store.getShortCode("layout"),
|
|
860
913
|
type: "layout",
|
|
861
914
|
parent: ctx.parent,
|
|
862
915
|
handler: RootLayout,
|
|
863
|
-
loading: undefined,
|
|
864
916
|
transition: config,
|
|
865
|
-
|
|
866
|
-
revalidate: [],
|
|
867
|
-
errorBoundary: [],
|
|
868
|
-
notFoundBoundary: [],
|
|
869
|
-
layout: [],
|
|
870
|
-
parallel: {},
|
|
871
|
-
intercept: [],
|
|
872
|
-
loader: [],
|
|
873
|
-
} as EntryData;
|
|
874
|
-
|
|
875
|
-
const result = store.run(namespace, entry, children)?.flat(3);
|
|
917
|
+
} satisfies EntryData;
|
|
876
918
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
919
|
+
const result = runAndValidateUseItems(
|
|
920
|
+
store,
|
|
921
|
+
namespace,
|
|
922
|
+
entry,
|
|
923
|
+
children,
|
|
924
|
+
"transition",
|
|
925
|
+
"children",
|
|
880
926
|
);
|
|
881
927
|
|
|
882
|
-
|
|
883
|
-
result &&
|
|
884
|
-
Array.isArray(result) &&
|
|
885
|
-
result.some((item) => hasRoutesInItem(item));
|
|
886
|
-
|
|
887
|
-
if (!hasRoutes) {
|
|
888
|
-
const parent = ctx.parent;
|
|
889
|
-
if (parent && "layout" in parent) {
|
|
890
|
-
entry.parent = null;
|
|
891
|
-
parent.layout.push(entry);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
928
|
+
if (isOrphan(result)) attachOrphanSibling(ctx.parent, entry);
|
|
894
929
|
|
|
895
930
|
return { name: namespace, type: "transition" } as TransitionItem;
|
|
896
931
|
};
|
|
897
932
|
|
|
898
|
-
const
|
|
899
|
-
const store =
|
|
900
|
-
|
|
901
|
-
|
|
933
|
+
const route: RouteHelpers<any, any>["route"] = (name, handler, use) => {
|
|
934
|
+
const { store, ctx } = requireDslContext(
|
|
935
|
+
"route() must be called inside urls()",
|
|
936
|
+
);
|
|
937
|
+
|
|
938
|
+
// Reject names colliding with reserved internal prefixes ($path_, $prefix_),
|
|
939
|
+
// the same guard path() and include() enforce. Without it such a name
|
|
940
|
+
// type-checks on an untyped router, then silently vanishes from generated
|
|
941
|
+
// route-types and public reverse() (isAutoGeneratedRouteName filters it out).
|
|
942
|
+
validateUserRouteName(name);
|
|
902
943
|
|
|
903
944
|
const namespace = `${ctx.namespace}.${store.getNextIndex("route")}.${name}`;
|
|
904
945
|
|
|
905
946
|
const entry = {
|
|
947
|
+
...emptySegmentBase(),
|
|
906
948
|
id: namespace,
|
|
907
949
|
shortCode: store.getShortCode("route"),
|
|
908
950
|
type: "route",
|
|
909
951
|
parent: ctx.parent,
|
|
910
952
|
handler: handler as unknown as Handler<any, any, any>,
|
|
911
|
-
loading: undefined, // Allow loading() to attach loading state
|
|
912
|
-
middleware: [],
|
|
913
|
-
revalidate: [],
|
|
914
|
-
errorBoundary: [],
|
|
915
|
-
notFoundBoundary: [],
|
|
916
|
-
layout: [],
|
|
917
|
-
parallel: {},
|
|
918
|
-
intercept: [],
|
|
919
|
-
loader: [],
|
|
920
953
|
} satisfies EntryData;
|
|
921
954
|
|
|
922
955
|
/* We will throw if user is registring same route name twice */
|
|
@@ -931,10 +964,13 @@ const routeFn: RouteHelpers<any, any>["route"] = (name, handler, use) => {
|
|
|
931
964
|
const mergedUse = mergeHandlerUse(handlerUseFn, use, "route");
|
|
932
965
|
/* Run use and attach handlers */
|
|
933
966
|
if (mergedUse) {
|
|
934
|
-
const result =
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
967
|
+
const result = runAndValidateUseItems(
|
|
968
|
+
store,
|
|
969
|
+
namespace,
|
|
970
|
+
entry,
|
|
971
|
+
mergedUse,
|
|
972
|
+
"route",
|
|
973
|
+
"use",
|
|
938
974
|
);
|
|
939
975
|
return { name: namespace, type: "route", uses: result } as RouteItem;
|
|
940
976
|
}
|
|
@@ -944,9 +980,9 @@ const routeFn: RouteHelpers<any, any>["route"] = (name, handler, use) => {
|
|
|
944
980
|
};
|
|
945
981
|
|
|
946
982
|
const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
947
|
-
const store =
|
|
948
|
-
|
|
949
|
-
|
|
983
|
+
const { store, ctx } = requireDslContext(
|
|
984
|
+
"layout() must be called inside urls()",
|
|
985
|
+
);
|
|
950
986
|
|
|
951
987
|
invariant(
|
|
952
988
|
!ctx.parent || ctx.parent.type !== "parallel",
|
|
@@ -964,20 +1000,12 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
964
1000
|
|
|
965
1001
|
const urlPrefix = getUrlPrefix();
|
|
966
1002
|
const entry = {
|
|
1003
|
+
...emptySegmentBase(),
|
|
967
1004
|
id: namespace,
|
|
968
1005
|
shortCode,
|
|
969
1006
|
type: "layout",
|
|
970
1007
|
parent: ctx.parent,
|
|
971
1008
|
handler: unwrappedHandler,
|
|
972
|
-
loading: undefined, // Allow loading() to attach loading state
|
|
973
|
-
middleware: [],
|
|
974
|
-
revalidate: [],
|
|
975
|
-
errorBoundary: [],
|
|
976
|
-
notFoundBoundary: [],
|
|
977
|
-
parallel: {},
|
|
978
|
-
intercept: [],
|
|
979
|
-
layout: [],
|
|
980
|
-
loader: [],
|
|
981
1009
|
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
982
1010
|
...(isStatic
|
|
983
1011
|
? {
|
|
@@ -999,11 +1027,13 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
999
1027
|
// Run merged use callback if present
|
|
1000
1028
|
let result: AllUseItems[] | undefined;
|
|
1001
1029
|
if (mergedUse) {
|
|
1002
|
-
result =
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1030
|
+
result = runAndValidateUseItems(
|
|
1031
|
+
store,
|
|
1032
|
+
namespace,
|
|
1033
|
+
entry,
|
|
1034
|
+
mergedUse,
|
|
1035
|
+
"layout",
|
|
1036
|
+
"use",
|
|
1007
1037
|
);
|
|
1008
1038
|
}
|
|
1009
1039
|
|
|
@@ -1045,9 +1075,7 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
1045
1075
|
`Orphan layouts can only be defined inside route or layout > check [${namespace}]`,
|
|
1046
1076
|
);
|
|
1047
1077
|
|
|
1048
|
-
|
|
1049
|
-
entry.parent = null;
|
|
1050
|
-
parent.layout.push(entry);
|
|
1078
|
+
attachOrphanSibling(parent, entry);
|
|
1051
1079
|
}
|
|
1052
1080
|
}
|
|
1053
1081
|
|
|
@@ -1060,33 +1088,15 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
1060
1088
|
} as LayoutItem;
|
|
1061
1089
|
};
|
|
1062
1090
|
|
|
1063
|
-
const isValidUseItem = (item: any): item is AllUseItems | undefined | null =>
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
(item
|
|
1068
|
-
typeof item === "object" &&
|
|
1069
|
-
"type" in item &&
|
|
1070
|
-
[
|
|
1071
|
-
"layout",
|
|
1072
|
-
"route",
|
|
1073
|
-
"middleware",
|
|
1074
|
-
"revalidate",
|
|
1075
|
-
"parallel",
|
|
1076
|
-
"intercept",
|
|
1077
|
-
"loader",
|
|
1078
|
-
"loading",
|
|
1079
|
-
"errorBoundary",
|
|
1080
|
-
"notFoundBoundary",
|
|
1081
|
-
"when",
|
|
1082
|
-
"cache",
|
|
1083
|
-
"transition",
|
|
1084
|
-
"include", // For urls() include() helper
|
|
1085
|
-
].includes(item.type))
|
|
1086
|
-
);
|
|
1087
|
-
};
|
|
1091
|
+
const isValidUseItem = (item: any): item is AllUseItems | undefined | null =>
|
|
1092
|
+
item == null ||
|
|
1093
|
+
(typeof item === "object" &&
|
|
1094
|
+
"type" in item &&
|
|
1095
|
+
ALL_USE_ITEM_TYPES.has(item.type));
|
|
1088
1096
|
|
|
1089
|
-
//
|
|
1097
|
+
// DSL helpers exported for direct import from @rangojs/router and for
|
|
1098
|
+
// assembly into the RouteHelpers object in helper-factories.ts. The route-item
|
|
1099
|
+
// types are discriminated by their `type` literal, so the helpers carry no brand.
|
|
1090
1100
|
export {
|
|
1091
1101
|
layout,
|
|
1092
1102
|
cache,
|
|
@@ -1094,28 +1104,13 @@ export {
|
|
|
1094
1104
|
revalidate,
|
|
1095
1105
|
parallel,
|
|
1096
1106
|
intercept,
|
|
1097
|
-
when,
|
|
1098
1107
|
errorBoundary,
|
|
1099
1108
|
notFoundBoundary,
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
const isOrphanLayout = (item: AllUseItems): boolean => {
|
|
1106
|
-
return (
|
|
1107
|
-
item.type === "layout" &&
|
|
1108
|
-
!item.uses?.some((child) => hasRoutesInItem(child))
|
|
1109
|
-
);
|
|
1110
|
-
};
|
|
1111
|
-
|
|
1112
|
-
// Internal exports used by helper-factories.ts
|
|
1113
|
-
export {
|
|
1114
|
-
routeFn,
|
|
1115
|
-
loaderFn,
|
|
1116
|
-
loadingFn,
|
|
1117
|
-
transitionFn,
|
|
1118
|
-
hasRoutesInItem,
|
|
1109
|
+
route,
|
|
1110
|
+
loader,
|
|
1111
|
+
loading,
|
|
1112
|
+
transition,
|
|
1119
1113
|
isValidUseItem,
|
|
1120
|
-
|
|
1114
|
+
emptySegmentBase,
|
|
1115
|
+
runAndValidateUseItems,
|
|
1121
1116
|
};
|