akanjs 3.0.2 → 3.1.0-rc.0
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/CHANGELOG.md +26 -0
- package/base/baseEnv.ts +0 -4
- package/base/primitiveRegistry.ts +2 -2
- package/client/cookie.ts +5 -1
- package/client/csrLocation.ts +122 -0
- package/client/csrTypes.ts +32 -4
- package/client/device.ts +2 -1
- package/client/frameConfig.ts +78 -7
- package/client/index.ts +3 -0
- package/client/makePageProto.tsx +3 -2
- package/client/native/external-view.ts +1 -0
- package/client/route/RootLayoutDefinition.ts +7 -1
- package/client/route/RouteHead.ts +108 -0
- package/client/route/index.ts +1 -0
- package/client/router.ts +36 -23
- package/client/rscNavigation.ts +8 -2
- package/client/viewportMeta.ts +60 -0
- package/client/webManifest.ts +18 -0
- package/common/Logger.ts +7 -2
- package/common/PipelineNames.ts +31 -0
- package/common/PipelinePattern.ts +70 -0
- package/common/PipelinePlanSchema.ts +490 -0
- package/common/backoff.ts +14 -0
- package/common/index.ts +39 -0
- package/common/pipelinePlan.ts +259 -0
- package/common/routeConvention.ts +1 -0
- package/common/types.ts +17 -0
- package/constant/constantRegistry.ts +26 -6
- package/dictionary/base.dictionary.ts +8 -0
- package/dictionary/trans.ts +9 -1
- package/document/into.ts +5 -0
- package/fetch/client/fetchClient.ts +135 -31
- package/fetch/client/httpClient.ts +45 -0
- package/fetch/client/wsClient.ts +125 -16
- package/fetch/fetchType/buildFetch.type.ts +1 -0
- package/index.ts +243 -10
- package/package.json +1 -5
- package/server/akanApp.ts +5 -2
- package/server/akanServer.ts +44 -8
- package/server/artifact/builderRpc.ts +8 -3
- package/server/artifact/ipcTypes.ts +5 -2
- package/server/di/diLifecycle.ts +39 -3
- package/server/di/utils.ts +28 -1
- package/server/ed25519Key.ts +48 -0
- package/server/index.ts +1 -0
- package/server/machine/index.ts +2 -0
- package/server/machine/machineAccount.ts +27 -0
- package/server/machine/machineToken.ts +151 -0
- package/server/mcp/McpApps.ts +256 -0
- package/server/mcp/McpDispatcher.ts +31 -7
- package/server/mcp/McpRouter.ts +209 -46
- package/server/ops/opsToken.ts +3 -10
- package/server/resolver/database.resolver.ts +3 -0
- package/server/resolver/signal.resolver.ts +40 -3
- package/server/routeElementComposer.tsx +78 -2
- package/server/routeState.ts +6 -0
- package/server/routeTreeBuilder.ts +26 -9
- package/server/routing/apiRouter.ts +6 -3
- package/server/routing/roomReplay.ts +88 -0
- package/server/rscClient.tsx +53 -8
- package/server/rscWorker.tsx +141 -27
- package/server/rscWorkerHost.ts +18 -5
- package/server/sitemap.ts +14 -0
- package/server/ssrFromRscRenderer.tsx +12 -6
- package/server/ssrTypes.ts +3 -1
- package/server/systemPageDocument.tsx +38 -30
- package/server/types.tsx +2 -0
- package/server/webRouter.ts +74 -5
- package/service/injectInfo.ts +9 -1
- package/service/ipcTypes.ts +2 -1
- package/service/predefinedAdaptor/database.adaptor.ts +1 -146
- package/service/predefinedAdaptor/queue.adaptor.ts +44 -6
- package/service/predefinedAdaptor/queueBackoff.ts +12 -0
- package/service/predefinedAdaptor/solidQueue.adaptor.ts +17 -13
- package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +44 -2
- package/service/predefinedAdaptor/sql/dialect/postgres.ts +3 -0
- package/service/predefinedAdaptor/sql/dialect/sqlite.ts +5 -2
- package/service/predefinedAdaptor/sql/search/fts5.ts +1 -1
- package/service/predefinedAdaptor/sql/types.ts +8 -6
- package/service/predefinedAdaptor/sql/values.ts +0 -23
- package/service/serve.ts +5 -1
- package/signal/agent.signal.ts +1 -1
- package/signal/base.signal.ts +2 -2
- package/signal/endpointIdempotency.ts +53 -0
- package/signal/internalArg.ts +14 -0
- package/signal/mcp/McpDocument.ts +38 -1
- package/signal/mcp/mcpProtocol.ts +18 -0
- package/signal/signalContext.ts +58 -30
- package/signal/signalRegistry.ts +14 -4
- package/signal/types.ts +18 -3
- package/store/agent/StoreSurfaceSource.ts +1 -1
- package/store/agentic/StToolBuilder.ts +3 -0
- package/test/conformance.ts +9 -33
- package/types/base/baseEnv.d.ts +0 -4
- package/types/base/primitiveRegistry.d.ts +1 -1
- package/types/client/csrLocation.d.ts +32 -0
- package/types/client/csrTypes.d.ts +40 -4
- package/types/client/frameConfig.d.ts +14 -2
- package/types/client/index.d.ts +3 -0
- package/types/client/native/external-view.d.ts +1 -0
- package/types/client/route/RootLayoutDefinition.d.ts +2 -1
- package/types/client/route/RouteHead.d.ts +13 -0
- package/types/client/route/index.d.ts +1 -0
- package/types/client/router.d.ts +6 -0
- package/types/client/rscNavigation.d.ts +4 -0
- package/types/client/viewportMeta.d.ts +18 -0
- package/types/client/webManifest.d.ts +4 -0
- package/types/common/PipelineNames.d.ts +6 -0
- package/types/common/PipelinePattern.d.ts +14 -0
- package/types/common/PipelinePlanSchema.d.ts +9 -0
- package/types/common/backoff.d.ts +10 -0
- package/types/common/index.d.ts +5 -0
- package/types/common/pipelinePlan.d.ts +224 -0
- package/types/common/types.d.ts +16 -0
- package/types/constant/constantRegistry.d.ts +5 -2
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictionary.d.ts +8 -8
- package/types/document/into.d.ts +9 -0
- package/types/fetch/client/fetchClient.d.ts +31 -4
- package/types/fetch/client/httpClient.d.ts +8 -0
- package/types/fetch/client/wsClient.d.ts +26 -2
- package/types/fetch/fetchType/buildFetch.type.d.ts +1 -0
- package/types/index.d.ts +230 -9
- package/types/server/akanServer.d.ts +16 -1
- package/types/server/artifact/builderRpc.d.ts +7 -2
- package/types/server/artifact/ipcTypes.d.ts +5 -1
- package/types/server/di/utils.d.ts +1 -0
- package/types/server/ed25519Key.d.ts +13 -0
- package/types/server/index.d.ts +1 -0
- package/types/server/machine/index.d.ts +2 -0
- package/types/server/machine/machineAccount.d.ts +5 -0
- package/types/server/machine/machineToken.d.ts +29 -0
- package/types/server/machine.d.ts +1 -0
- package/types/server/mcp/McpApps.d.ts +50 -0
- package/types/server/mcp/McpDispatcher.d.ts +4 -1
- package/types/server/mcp/McpRouter.d.ts +4 -0
- package/types/server/resolver/signal.resolver.d.ts +2 -1
- package/types/server/routeElementComposer.d.ts +24 -4
- package/types/server/routeState.d.ts +4 -1
- package/types/server/routing/roomReplay.d.ts +24 -0
- package/types/server/rscWorkerHost.d.ts +5 -2
- package/types/server/sitemap.d.ts +2 -0
- package/types/server/ssrFromRscRenderer.d.ts +3 -3
- package/types/server/ssrTypes.d.ts +4 -1
- package/types/server/systemPageDocument.d.ts +2 -0
- package/types/server/types.d.ts +4 -1
- package/types/server/webRouter.d.ts +3 -1
- package/types/service/ipcTypes.d.ts +2 -0
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +1 -24
- package/types/service/predefinedAdaptor/queue.adaptor.d.ts +10 -3
- package/types/service/predefinedAdaptor/queueBackoff.d.ts +4 -0
- package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +3 -3
- package/types/service/predefinedAdaptor/sql/SqlDocumentStore.d.ts +6 -1
- package/types/service/predefinedAdaptor/sql/dialect/postgres.d.ts +1 -0
- package/types/service/predefinedAdaptor/sql/dialect/sqlite.d.ts +1 -0
- package/types/service/predefinedAdaptor/sql/search/fts5.d.ts +1 -1
- package/types/service/predefinedAdaptor/sql/types.d.ts +9 -5
- package/types/service/predefinedAdaptor/sql/values.d.ts +0 -3
- package/types/service/serve.d.ts +3 -0
- package/types/signal/endpointIdempotency.d.ts +7 -0
- package/types/signal/internalArg.d.ts +10 -0
- package/types/signal/mcp/McpDocument.d.ts +8 -0
- package/types/signal/mcp/mcpProtocol.d.ts +17 -0
- package/types/signal/signalContext.d.ts +5 -3
- package/types/signal/signalRegistry.d.ts +4 -4
- package/types/signal/types.d.ts +21 -1
- package/types/store/agentic/StToolBuilder.d.ts +2 -0
- package/types/test/conformance.d.ts +7 -10
- package/types/ui/Agent/Chat.d.ts +5 -1
- package/types/ui/Agent/EmbedApproval.d.ts +1 -0
- package/types/ui/Agent/index.d.ts +3 -0
- package/types/ui/Agent/useKeyboardFrame.d.ts +2 -0
- package/types/ui/Agent/useScreenActions.d.ts +18 -0
- package/types/ui/BottomSheet.d.ts +1 -0
- package/types/ui/Constant/Doc.d.ts +11 -17
- package/types/ui/Constant/index.d.ts +7 -2
- package/types/ui/Constant/schemaDoc.d.ts +11 -0
- package/types/ui/Dialog/Modal.d.ts +6 -1
- package/types/ui/Dialog/index.d.ts +1 -1
- package/types/ui/Dialog/modalSurface.d.ts +12 -0
- package/types/ui/Layout/BottomTab.d.ts +11 -2
- package/types/ui/Layout/FrameSlotFallback.d.ts +9 -0
- package/types/ui/Layout/Navbar.d.ts +4 -1
- package/types/ui/Layout/TopInset.d.ts +1 -1
- package/types/ui/Layout/index.d.ts +3 -3
- package/types/ui/Link/Action.d.ts +5 -1
- package/types/ui/Link/Anchor.d.ts +5 -3
- package/types/ui/Link/activePath.d.ts +13 -0
- package/types/ui/Link/index.d.ts +1 -1
- package/types/ui/Modal.d.ts +6 -1
- package/types/ui/Model/EditModal.d.ts +6 -1
- package/types/ui/Model/New.d.ts +6 -1
- package/types/ui/Model/ViewEditModal.d.ts +6 -1
- package/types/ui/Model/ViewModal.d.ts +6 -1
- package/types/ui/Model/newFormOrigin.d.ts +7 -0
- package/types/ui/Portal.d.ts +3 -1
- package/types/ui/Reference/origin.d.ts +12 -0
- package/types/ui/Signal/Doc.d.ts +23 -18
- package/types/ui/Signal/Listener.d.ts +2 -6
- package/types/ui/Signal/Object.d.ts +6 -10
- package/types/ui/Signal/Response.d.ts +3 -6
- package/types/ui/Signal/RestApi.d.ts +11 -15
- package/types/ui/Signal/WebSocket.d.ts +11 -21
- package/types/ui/Signal/endpointEntries.d.ts +8 -0
- package/types/ui/Signal/index.d.ts +35 -12
- package/types/ui/Signal/signalText.d.ts +178 -0
- package/types/ui/System/CSR.d.ts +2 -2
- package/types/ui/System/Client.d.ts +4 -3
- package/types/ui/System/Common.d.ts +27 -6
- package/types/ui/System/SSR.d.ts +6 -6
- package/types/ui/Tooltip.d.ts +4 -2
- package/types/ui/index.d.ts +2 -1
- package/types/ui/overlayLayer.d.ts +6 -0
- package/types/ui/overlayPosition.d.ts +17 -0
- package/types/vendor/@akanjs/native/packages/cli/src/api.d.ts +14 -3
- package/types/vendor/@akanjs/native/packages/cli/src/commands/update.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/cli/src/config.d.ts +30 -5
- package/types/vendor/@akanjs/native/packages/cli/src/lib/compat.d.ts +8 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.d.ts +5 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/distribution.d.ts +22 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/project.d.ts +4 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/publish.d.ts +16 -10
- package/types/vendor/@akanjs/native/packages/cli/src/lib/updates.d.ts +51 -7
- package/types/vendor/@akanjs/native/packages/cli/src/lib/vectors.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.d.ts +7 -0
- package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows.d.ts +5 -0
- package/types/vendor/@akanjs/native/packages/core/src/kernel.d.ts +36 -0
- package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +2 -0
- package/types/vendor/@akanjs/native/packages/desktop/src/server.d.ts +11 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src/desktop.d.ts +17 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src/index.d.ts +132 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src.d.ts +1 -0
- package/types/vendor/@akanjs/native/plugins/updates/src/index.d.ts +8 -0
- package/types/vendor/@akanjs/native/plugins/window/src/desktop.d.ts +4 -1
- package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +49 -0
- package/types/vendor/use-agentic/types.d.ts +2 -0
- package/types/webkit/CsrStack.d.ts +1 -1
- package/types/webkit/McpAppAgent.d.ts +14 -0
- package/types/webkit/McpAppApprovals.d.ts +8 -0
- package/types/webkit/McpAppBridge.d.ts +101 -0
- package/types/webkit/McpAppView.d.ts +21 -0
- package/types/webkit/index.d.ts +1 -0
- package/types/webkit/useLocation.d.ts +1 -1
- package/types/webkit/useVisualViewportFrame.d.ts +13 -0
- package/ui/Agent/Chat.tsx +29 -10
- package/ui/Agent/Composer.tsx +2 -1
- package/ui/Agent/EmbedApproval.tsx +17 -0
- package/ui/Agent/RichInput.tsx +1 -1
- package/ui/Agent/index.ts +2 -0
- package/ui/Agent/useKeyboardFrame.ts +10 -0
- package/ui/Agent/useScreenActions.ts +45 -0
- package/ui/BottomSheet.tsx +82 -53
- package/ui/Constant/Doc.tsx +113 -60
- package/ui/Constant/index.ts +2 -2
- package/ui/Constant/schemaDoc.ts +86 -26
- package/ui/Dialog/LegacyModal.tsx +53 -52
- package/ui/Dialog/Modal.tsx +93 -49
- package/ui/Dialog/modalSurface.ts +61 -0
- package/ui/DragAction.tsx +1 -1
- package/ui/DraggableList.tsx +1 -1
- package/ui/Dropdown.tsx +4 -6
- package/ui/KeyboardAvoiding.tsx +1 -1
- package/ui/Layout/BottomInset.tsx +20 -14
- package/ui/Layout/BottomTab.tsx +42 -16
- package/ui/Layout/FrameSlotFallback.tsx +59 -0
- package/ui/Layout/Navbar.tsx +37 -11
- package/ui/Layout/Sider.tsx +1 -1
- package/ui/Layout/TopInset.tsx +15 -14
- package/ui/Link/Action.tsx +15 -5
- package/ui/Link/Anchor.tsx +25 -13
- package/ui/Link/activePath.ts +25 -0
- package/ui/Link/index.tsx +1 -1
- package/ui/Load/Pagination.tsx +1 -1
- package/ui/Load/Units.tsx +1 -1
- package/ui/Loading/ProgressBar.tsx +1 -1
- package/ui/Modal.tsx +9 -0
- package/ui/Model/EditModal.tsx +16 -1
- package/ui/Model/New.tsx +25 -2
- package/ui/Model/NewWrapper_Client.tsx +2 -0
- package/ui/Model/ViewEditModal.tsx +9 -0
- package/ui/Model/ViewModal.tsx +16 -1
- package/ui/Model/newFormOrigin.ts +45 -0
- package/ui/Popconfirm.tsx +5 -7
- package/ui/Portal.tsx +4 -7
- package/ui/Reference/origin.ts +31 -0
- package/ui/ScreenNavigator.tsx +1 -1
- package/ui/Select.tsx +2 -7
- package/ui/Signal/Arg.tsx +2 -2
- package/ui/Signal/Doc.tsx +125 -63
- package/ui/Signal/Endpoint.tsx +11 -10
- package/ui/Signal/Listener.tsx +7 -10
- package/ui/Signal/Object.tsx +7 -14
- package/ui/Signal/Response.tsx +24 -24
- package/ui/Signal/RestApi.tsx +63 -52
- package/ui/Signal/WebSocket.tsx +65 -72
- package/ui/Signal/endpointEntries.ts +25 -0
- package/ui/Signal/index.ts +23 -6
- package/ui/Signal/signalText.ts +59 -0
- package/ui/System/CSR.tsx +34 -8
- package/ui/System/Client.tsx +36 -5
- package/ui/System/Common.tsx +76 -32
- package/ui/System/SSR.tsx +73 -31
- package/ui/Toast.tsx +1 -1
- package/ui/Tooltip.tsx +84 -30
- package/ui/index.ts +11 -1
- package/ui/overlayLayer.ts +25 -2
- package/ui/overlayPosition.ts +90 -0
- package/ui/recipe/inputRecipe.ts +8 -1
- package/ui/styles.css +152 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeActivity.kt +22 -2
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeKernel.kt +37 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdateReports.kt +169 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdates.kt +110 -38
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeVectors.kt +20 -0
- package/vendor/@akanjs/native/native/desktop/Cargo.lock +16 -16
- package/vendor/@akanjs/native/native/desktop/src/external.rs +651 -0
- package/vendor/@akanjs/native/native/desktop/src/json.rs +23 -0
- package/vendor/@akanjs/native/native/desktop/src/lib.rs +228 -5
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeApp.swift +2 -1
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeKernel.swift +47 -1
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdateReports.swift +145 -0
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdates.swift +112 -43
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeVectors.swift +18 -0
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeViewController.swift +24 -2
- package/vendor/@akanjs/native/packages/cli/src/api.ts +22 -6
- package/vendor/@akanjs/native/packages/cli/src/commands/build.ts +24 -8
- package/vendor/@akanjs/native/packages/cli/src/commands/update.ts +11 -9
- package/vendor/@akanjs/native/packages/cli/src/config.ts +35 -7
- package/vendor/@akanjs/native/packages/cli/src/lib/compat.ts +2 -0
- package/vendor/@akanjs/native/packages/cli/src/lib/configkeys.ts +6 -4
- package/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.ts +11 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/distribution.ts +73 -0
- package/vendor/@akanjs/native/packages/cli/src/lib/project.ts +15 -3
- package/vendor/@akanjs/native/packages/cli/src/lib/publish.ts +70 -23
- package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +159 -23
- package/vendor/@akanjs/native/packages/cli/src/lib/vectors.ts +2 -0
- package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +9 -4
- package/vendor/@akanjs/native/packages/cli/src/platforms/desktop-update.ts +1 -1
- package/vendor/@akanjs/native/packages/cli/src/platforms/desktop.ts +4 -2
- package/vendor/@akanjs/native/packages/cli/src/platforms/ios.ts +9 -4
- package/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.ts +25 -11
- package/vendor/@akanjs/native/packages/cli/src/platforms/windows.ts +14 -3
- package/vendor/@akanjs/native/packages/core/src/kernel.ts +49 -0
- package/vendor/@akanjs/native/packages/core/vectors/updates.json +31 -0
- package/vendor/@akanjs/native/packages/desktop/src/main.ts +3 -0
- package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +2 -0
- package/vendor/@akanjs/native/packages/desktop/src/server.ts +70 -6
- package/vendor/@akanjs/native/plugins/external-view/native-plugin.json +23 -0
- package/vendor/@akanjs/native/plugins/external-view/src/desktop.ts +321 -0
- package/vendor/@akanjs/native/plugins/external-view/src/index.ts +106 -0
- package/vendor/@akanjs/native/plugins/updates/android/UpdatesPlugin.kt +133 -41
- package/vendor/@akanjs/native/plugins/updates/ios/UpdatesPlugin.swift +101 -36
- package/vendor/@akanjs/native/plugins/updates/native-plugin.json +4 -2
- package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +15 -1
- package/vendor/@akanjs/native/plugins/updates/src/index.ts +7 -1
- package/vendor/@akanjs/native/plugins/window/native-plugin.json +4 -0
- package/vendor/@akanjs/native/plugins/window/src/desktop.ts +68 -1
- package/vendor/@akanjs/native/plugins/window/src/index.ts +47 -0
- package/vendor/use-agentic/ToolRunner.ts +8 -1
- package/vendor/use-agentic/types.ts +2 -0
- package/webkit/CsrRouteTable.ts +1 -0
- package/webkit/CsrStack.ts +9 -2
- package/webkit/McpAppAgent.ts +138 -0
- package/webkit/McpAppApprovals.ts +56 -0
- package/webkit/McpAppBridge.ts +291 -0
- package/webkit/McpAppView.ts +258 -0
- package/webkit/bootCsr.tsx +54 -5
- package/webkit/index.ts +1 -0
- package/webkit/useCsrValues.ts +16 -10
- package/webkit/useFrameRuntime.ts +10 -1
- package/webkit/useLocation.ts +4 -3
- package/webkit/useVisualViewportFrame.tsx +52 -0
- package/service/predefinedAdaptor/sql/driver/libsql.ts +0 -45
- package/types/service/predefinedAdaptor/sql/driver/libsql.d.ts +0 -20
- package/types/ui/Agent/useKeyboardInset.d.ts +0 -1
- package/types/ui/ServerPortal.d.ts +0 -15
- package/ui/Agent/useKeyboardInset.ts +0 -19
- package/ui/ServerPortal.tsx +0 -49
package/server/rscWorker.tsx
CHANGED
|
@@ -6,6 +6,8 @@ import type {
|
|
|
6
6
|
PathRoute,
|
|
7
7
|
RedirectStatus,
|
|
8
8
|
ResolvedHead,
|
|
9
|
+
RouteRender,
|
|
10
|
+
RouteViewport,
|
|
9
11
|
} from "akanjs/client";
|
|
10
12
|
import { type AkanI18nConfig, DEFAULT_AKAN_I18N, getBasePathFromPathname, Logger } from "akanjs/common";
|
|
11
13
|
import {
|
|
@@ -24,6 +26,8 @@ import {
|
|
|
24
26
|
} from "akanjs/fetch";
|
|
25
27
|
import type { ReactNode } from "react";
|
|
26
28
|
import { renderToReadableStream } from "react-server-dom-webpack/server.node";
|
|
29
|
+
import { ViewportMeta } from "../client/viewportMeta";
|
|
30
|
+
import { toManifestJson, webManifestHref } from "../client/webManifest";
|
|
27
31
|
import type { PagePromptRunInput } from "../signal/mcp/pagePrompt";
|
|
28
32
|
import { getCurrentTrace, runTraced, SignalTrace } from "../signal/trace";
|
|
29
33
|
import type { ClientManifest } from "./artifact";
|
|
@@ -40,7 +44,7 @@ import {
|
|
|
40
44
|
import { createAkanLocaleAlternateHeadSnapshot, mergeAkanHeadSnapshots, renderAkanHeadSnapshot } from "./head";
|
|
41
45
|
import { LogForwarder } from "./logging/logForwarder";
|
|
42
46
|
import { ProcessMetricsCollector } from "./processMetricsCollector";
|
|
43
|
-
import { RouteElementComposer } from "./routeElementComposer";
|
|
47
|
+
import { RouteElementComposer, type RouteNotFoundInPlace } from "./routeElementComposer";
|
|
44
48
|
import {
|
|
45
49
|
type AkanRscPatchDecision,
|
|
46
50
|
createAkanRouterState,
|
|
@@ -69,7 +73,12 @@ import {
|
|
|
69
73
|
shouldUseRscWorkerFullResultCache,
|
|
70
74
|
} from "./rscWorkerCache";
|
|
71
75
|
import type { RscTraceMetadata } from "./ssrTypes";
|
|
72
|
-
import {
|
|
76
|
+
import {
|
|
77
|
+
createSystemPageDocument,
|
|
78
|
+
getPathnameLocale,
|
|
79
|
+
getSystemPageHomeHref,
|
|
80
|
+
SystemPageMain,
|
|
81
|
+
} from "./systemPageDocument";
|
|
73
82
|
|
|
74
83
|
interface InitMsg {
|
|
75
84
|
type: "init";
|
|
@@ -123,6 +132,11 @@ interface PagePromptRunMsg {
|
|
|
123
132
|
requestId: string;
|
|
124
133
|
input: PagePromptRunInput;
|
|
125
134
|
}
|
|
135
|
+
interface WebManifestMsg {
|
|
136
|
+
type: "web-manifest";
|
|
137
|
+
requestId: string;
|
|
138
|
+
basePath: string | null;
|
|
139
|
+
}
|
|
126
140
|
type InMsg =
|
|
127
141
|
| InitMsg
|
|
128
142
|
| RenderMsg
|
|
@@ -132,7 +146,8 @@ type InMsg =
|
|
|
132
146
|
| InvalidateCacheMsg
|
|
133
147
|
| LogLevelMsg
|
|
134
148
|
| PagePromptsMsg
|
|
135
|
-
| PagePromptRunMsg
|
|
149
|
+
| PagePromptRunMsg
|
|
150
|
+
| WebManifestMsg;
|
|
136
151
|
type RenderControl =
|
|
137
152
|
| { type: "redirect"; location: string; method: "replace" | "push"; status: RedirectStatus }
|
|
138
153
|
| { type: "not-found" }
|
|
@@ -306,9 +321,20 @@ export class RscRenderer {
|
|
|
306
321
|
case "page-prompt.run":
|
|
307
322
|
void this.#answer(msg.requestId, "page-prompt.result", () => this.#pagePrompts.run(msg.input));
|
|
308
323
|
return;
|
|
324
|
+
case "web-manifest":
|
|
325
|
+
void this.#answer(msg.requestId, "web-manifest.result", () => this.#webManifest(msg.basePath));
|
|
326
|
+
return;
|
|
309
327
|
}
|
|
310
328
|
}
|
|
311
329
|
|
|
330
|
+
async #webManifest(basePath: string | null): Promise<unknown> {
|
|
331
|
+
const pathname = `/${this.#i18n.defaultLocale}${basePath ? `/${basePath}` : ""}`;
|
|
332
|
+
const rootLayouts = RouteTreeBuilder.matchFallback(pathname, this.#fallbackRoutes)?.fallbackRoute.renderRootLayouts;
|
|
333
|
+
const manifests = await Promise.all((rootLayouts ?? []).map((render) => render.getLayoutManifest?.()));
|
|
334
|
+
const manifest = manifests.find((entry) => entry !== undefined);
|
|
335
|
+
return manifest ? toManifestJson(manifest) : null;
|
|
336
|
+
}
|
|
337
|
+
|
|
312
338
|
async #answer(requestId: string, type: string, fn: () => Promise<unknown>): Promise<void> {
|
|
313
339
|
try {
|
|
314
340
|
this.#send({ type, requestId, result: await fn() });
|
|
@@ -458,11 +484,18 @@ export class RscRenderer {
|
|
|
458
484
|
const beforeLoadedKeys = RouteTreeBuilder.getCacheStats().loadedModuleKeys;
|
|
459
485
|
const cacheDecision = match ? this.#getResultCacheEntry(request, urlObj) : { entry: null };
|
|
460
486
|
const cacheEntry = cacheDecision.entry;
|
|
487
|
+
const targetFrame = match
|
|
488
|
+
? await RouteElementComposer.resolveSsrFramePathRoute({
|
|
489
|
+
pathRoute: match.pathRoute,
|
|
490
|
+
basePath: this.#getBasePath(urlObj),
|
|
491
|
+
})
|
|
492
|
+
: null;
|
|
461
493
|
const targetRouterState = match
|
|
462
494
|
? createAkanRouterState({
|
|
463
495
|
pathRoute: match.pathRoute,
|
|
464
496
|
href: urlObj.href,
|
|
465
497
|
buildId: this.#pagesBundleBuildId,
|
|
498
|
+
transition: targetFrame?.pageState.transition,
|
|
466
499
|
})
|
|
467
500
|
: null;
|
|
468
501
|
const searchParams = RouteTreeBuilder.parseSearchParams(urlObj.search);
|
|
@@ -569,12 +602,15 @@ export class RscRenderer {
|
|
|
569
602
|
}
|
|
570
603
|
let element: ReactNode;
|
|
571
604
|
let effectivePatchDecision = safePatchDecision;
|
|
605
|
+
const notFoundProbe = { hit: false };
|
|
606
|
+
const notFound = this.#notFoundInPlace(urlObj, notFoundProbe);
|
|
572
607
|
if (match && safePatchDecision.status === "patch" && safePatchDecision.patch) {
|
|
573
608
|
const suffixElement = await this.#renderMatchedSuffix(
|
|
574
609
|
urlObj,
|
|
575
610
|
match,
|
|
576
611
|
safePatchDecision.patch.patchStartIndex,
|
|
577
612
|
searchParams,
|
|
613
|
+
notFound,
|
|
578
614
|
);
|
|
579
615
|
if (suffixElement === null) {
|
|
580
616
|
effectivePatchDecision = {
|
|
@@ -582,9 +618,9 @@ export class RscRenderer {
|
|
|
582
618
|
reason: "suffix-compose-fallback",
|
|
583
619
|
commonPrefixLength: safePatchDecision.commonPrefixLength,
|
|
584
620
|
};
|
|
585
|
-
element = await this.#renderMatched(urlObj, match, searchParams);
|
|
621
|
+
element = await this.#renderMatched(urlObj, match, searchParams, notFound);
|
|
586
622
|
} else element = suffixElement;
|
|
587
|
-
} else if (match) element = await this.#renderMatched(urlObj, match, searchParams);
|
|
623
|
+
} else if (match) element = await this.#renderMatched(urlObj, match, searchParams, notFound);
|
|
588
624
|
else element = await this.#renderNotFound(urlObj);
|
|
589
625
|
const traceCacheKey =
|
|
590
626
|
effectivePatchDecision.status === "patch" ? (patchCacheEntry?.key ?? cacheEntry?.key) : cacheEntry?.key;
|
|
@@ -604,6 +640,7 @@ export class RscRenderer {
|
|
|
604
640
|
}),
|
|
605
641
|
status: match ? undefined : 404,
|
|
606
642
|
trace,
|
|
643
|
+
notFoundProbe,
|
|
607
644
|
onComplete: ({ chunks, bytes, chunksCount, control, lateControlSent }) => {
|
|
608
645
|
const cacheState = shouldStoreRouteCache({
|
|
609
646
|
policy: getRequestPolicy(),
|
|
@@ -827,6 +864,8 @@ export class RscRenderer {
|
|
|
827
864
|
collectChunks?: boolean;
|
|
828
865
|
status?: number;
|
|
829
866
|
trace?: RscTraceMetadata;
|
|
867
|
+
/** Raised by a render that answered `router.notFound()` in place, which React never reports as an error. */
|
|
868
|
+
notFoundProbe?: { hit: boolean };
|
|
830
869
|
onComplete?: (
|
|
831
870
|
result: Omit<FlightRenderResult, "cancelled">,
|
|
832
871
|
) => Promise<RouteCacheRenderState> | RouteCacheRenderState;
|
|
@@ -881,17 +920,29 @@ export class RscRenderer {
|
|
|
881
920
|
reportedTheme = theme;
|
|
882
921
|
this.#send({ type: "theme", requestId: options.requestId, theme });
|
|
883
922
|
};
|
|
884
|
-
const
|
|
885
|
-
if (!
|
|
886
|
-
|
|
923
|
+
const readProbe = () => {
|
|
924
|
+
if (!controlRef.current && options.notFoundProbe?.hit) controlRef.current = { type: "not-found" };
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
const sendLateControl = () => {
|
|
928
|
+
const control = controlRef.current;
|
|
929
|
+
if (!options.requestId || lateControlSent || !control || control.type === "error") return;
|
|
887
930
|
lateControlSent = true;
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
931
|
+
if (control.type === "redirect") {
|
|
932
|
+
this.#send({
|
|
933
|
+
type: "late-redirect",
|
|
934
|
+
requestId: options.requestId,
|
|
935
|
+
location: control.location,
|
|
936
|
+
method: control.method,
|
|
937
|
+
status: control.status,
|
|
938
|
+
});
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
if (!options.notFoundProbe?.hit)
|
|
942
|
+
this.#logger.warn(
|
|
943
|
+
`render[${options.requestId}] not-found raised below a route render after the stream started; that subtree renders nothing — call router.notFound() from the page or layout body`,
|
|
944
|
+
);
|
|
945
|
+
this.#send({ type: "late-not-found", requestId: options.requestId });
|
|
895
946
|
};
|
|
896
947
|
try {
|
|
897
948
|
for (;;) {
|
|
@@ -900,12 +951,22 @@ export class RscRenderer {
|
|
|
900
951
|
return { chunks, bytes, chunksCount, control: null, lateControlSent, cancelled: true };
|
|
901
952
|
}
|
|
902
953
|
const { value, done } = await reader.read();
|
|
954
|
+
readProbe();
|
|
903
955
|
if (controlRef.current && !sentChunk) {
|
|
904
956
|
await reader.cancel();
|
|
905
957
|
return { chunks, bytes, chunksCount, control: controlRef.current, lateControlSent, cancelled: false };
|
|
906
958
|
}
|
|
907
|
-
if (controlRef.current && sentChunk)
|
|
959
|
+
if (controlRef.current && sentChunk) sendLateControl();
|
|
908
960
|
if (done) break;
|
|
961
|
+
|
|
962
|
+
if (!sentChunk && options.requestId) {
|
|
963
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
964
|
+
readProbe();
|
|
965
|
+
if (controlRef.current) {
|
|
966
|
+
await reader.cancel();
|
|
967
|
+
return { chunks, bytes, chunksCount, control: controlRef.current, lateControlSent, cancelled: false };
|
|
968
|
+
}
|
|
969
|
+
}
|
|
909
970
|
const chunk = value instanceof Uint8Array ? value : new Uint8Array(value as ArrayBufferLike);
|
|
910
971
|
bytes += chunk.byteLength;
|
|
911
972
|
chunksCount += 1;
|
|
@@ -926,7 +987,8 @@ export class RscRenderer {
|
|
|
926
987
|
if (options.requestId) this.#activeRenderReaders.delete(options.requestId);
|
|
927
988
|
reader.releaseLock();
|
|
928
989
|
}
|
|
929
|
-
|
|
990
|
+
readProbe();
|
|
991
|
+
if (controlRef.current && sentChunk) sendLateControl();
|
|
930
992
|
if (controlRef.current && !sentChunk)
|
|
931
993
|
return { chunks, bytes, chunksCount, control: controlRef.current, lateControlSent, cancelled: false };
|
|
932
994
|
if (options.requestId) {
|
|
@@ -1211,6 +1273,8 @@ export class RscRenderer {
|
|
|
1211
1273
|
digest,
|
|
1212
1274
|
});
|
|
1213
1275
|
if (!body) return null;
|
|
1276
|
+
const viewport = await RscRenderer.#rootViewport(route.renderRootLayouts);
|
|
1277
|
+
const manifestLink = await this.#renderManifestLink(url, route.renderRootLayouts);
|
|
1214
1278
|
const routeHead =
|
|
1215
1279
|
"resolveHead" in route
|
|
1216
1280
|
? await RouteElementComposer.resolveHeadWithSnapshot({
|
|
@@ -1224,7 +1288,8 @@ export class RscRenderer {
|
|
|
1224
1288
|
<html lang={params.lang ?? getPathnameLocale(pathname, this.#i18n)} suppressHydrationWarning>
|
|
1225
1289
|
<head key="head">
|
|
1226
1290
|
<meta key="charset" charSet="utf-8" />
|
|
1227
|
-
|
|
1291
|
+
{this.#renderViewport(viewport)}
|
|
1292
|
+
{manifestLink}
|
|
1228
1293
|
<meta key="robots" name="robots" content="noindex" />
|
|
1229
1294
|
{routeHeadSnapshot
|
|
1230
1295
|
? renderAkanHeadSnapshot(routeHeadSnapshot)
|
|
@@ -1240,7 +1305,8 @@ export class RscRenderer {
|
|
|
1240
1305
|
async #renderMatched(
|
|
1241
1306
|
url: URL,
|
|
1242
1307
|
match: { pathRoute: PathRoute; params: Record<string, string> },
|
|
1243
|
-
searchParams
|
|
1308
|
+
searchParams: Record<string, string | string[]>,
|
|
1309
|
+
notFound: RouteNotFoundInPlace,
|
|
1244
1310
|
): Promise<ReactNode> {
|
|
1245
1311
|
this.#logger.verbose(
|
|
1246
1312
|
`composing route element pathname=${url.pathname} search=${url.search || "(none)"} params=${JSON.stringify(match.params)}`,
|
|
@@ -1250,10 +1316,13 @@ export class RscRenderer {
|
|
|
1250
1316
|
basePath: this.#getBasePath(url),
|
|
1251
1317
|
});
|
|
1252
1318
|
setRequestFrameState(pathRoute.pageState);
|
|
1319
|
+
await RouteElementComposer.checkArgs({ pathRoute, params: match.params, searchParams });
|
|
1253
1320
|
|
|
1254
1321
|
const rootThemes = await Promise.all(pathRoute.renderRootLayouts.map((render) => render.getLayoutTheme?.()));
|
|
1255
1322
|
const rootTheme = rootThemes.find((theme) => theme !== undefined);
|
|
1256
1323
|
if (rootTheme) setRequestTheme(rootTheme);
|
|
1324
|
+
const viewport = await RscRenderer.#rootViewport(pathRoute.renderRootLayouts);
|
|
1325
|
+
const manifestLink = await this.#renderManifestLink(url, pathRoute.renderRootLayouts);
|
|
1257
1326
|
const routeHead = await RouteElementComposer.resolveHeadWithSnapshot({
|
|
1258
1327
|
pathRoute,
|
|
1259
1328
|
params: match.params,
|
|
@@ -1265,13 +1334,15 @@ export class RscRenderer {
|
|
|
1265
1334
|
params: match.params,
|
|
1266
1335
|
searchParams,
|
|
1267
1336
|
navKey: url.pathname + url.search,
|
|
1337
|
+
notFound,
|
|
1268
1338
|
});
|
|
1269
1339
|
|
|
1270
1340
|
return (
|
|
1271
1341
|
<html lang={match.params.lang ?? this.#i18n.defaultLocale} suppressHydrationWarning>
|
|
1272
1342
|
<head key="head">
|
|
1273
1343
|
<meta key="charset" charSet="utf-8" />
|
|
1274
|
-
|
|
1344
|
+
{this.#renderViewport(viewport, rootTheme)}
|
|
1345
|
+
{manifestLink}
|
|
1275
1346
|
{routeHeadSnapshot
|
|
1276
1347
|
? renderAkanHeadSnapshot(routeHeadSnapshot)
|
|
1277
1348
|
: (routeHead.node ?? this.#renderDefaultHead())}
|
|
@@ -1287,7 +1358,8 @@ export class RscRenderer {
|
|
|
1287
1358
|
url: URL,
|
|
1288
1359
|
match: { pathRoute: PathRoute; params: Record<string, string> },
|
|
1289
1360
|
patchStartIndex: number,
|
|
1290
|
-
searchParams
|
|
1361
|
+
searchParams: Record<string, string | string[]>,
|
|
1362
|
+
notFound: RouteNotFoundInPlace,
|
|
1291
1363
|
): Promise<ReactNode | null> {
|
|
1292
1364
|
this.#logger.verbose(
|
|
1293
1365
|
`composing route suffix pathname=${url.pathname} start=${patchStartIndex} params=${JSON.stringify(match.params)}`,
|
|
@@ -1297,6 +1369,7 @@ export class RscRenderer {
|
|
|
1297
1369
|
basePath: this.#getBasePath(url),
|
|
1298
1370
|
});
|
|
1299
1371
|
setRequestFrameState(pathRoute.pageState);
|
|
1372
|
+
await RouteElementComposer.checkArgs({ pathRoute, params: match.params, searchParams });
|
|
1300
1373
|
await RouteElementComposer.resolveSuffixLoadings(pathRoute, patchStartIndex);
|
|
1301
1374
|
return RouteElementComposer.composeSuffix({
|
|
1302
1375
|
pathRoute,
|
|
@@ -1304,6 +1377,7 @@ export class RscRenderer {
|
|
|
1304
1377
|
searchParams,
|
|
1305
1378
|
patchStartIndex,
|
|
1306
1379
|
navKey: url.pathname + url.search,
|
|
1380
|
+
notFound,
|
|
1307
1381
|
});
|
|
1308
1382
|
}
|
|
1309
1383
|
|
|
@@ -1329,21 +1403,61 @@ export class RscRenderer {
|
|
|
1329
1403
|
return this.#renderSystemNotFound(url);
|
|
1330
1404
|
}
|
|
1331
1405
|
|
|
1406
|
+
#notFoundInPlace(url: URL, probe: { hit: boolean }): RouteNotFoundInPlace {
|
|
1407
|
+
return {
|
|
1408
|
+
pathname: url.pathname,
|
|
1409
|
+
onNotFound: () => {
|
|
1410
|
+
probe.hit = true;
|
|
1411
|
+
},
|
|
1412
|
+
systemFallback: () => (
|
|
1413
|
+
<SystemPageMain kind="not-found" pathname={url.pathname} homeHref={this.#systemHomeHref(url)} />
|
|
1414
|
+
),
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
#systemHomeHref(url: URL): string {
|
|
1419
|
+
return getSystemPageHomeHref({
|
|
1420
|
+
pathname: url.pathname,
|
|
1421
|
+
i18n: this.#i18n,
|
|
1422
|
+
basePaths: this.#basePaths,
|
|
1423
|
+
headerBasePath: untrackedRequest()?.headers.get("x-base-path"),
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1332
1427
|
#renderSystemNotFound(url: URL): ReactNode {
|
|
1333
1428
|
return createSystemPageDocument({
|
|
1334
1429
|
kind: "not-found",
|
|
1335
1430
|
pathname: url.pathname,
|
|
1336
1431
|
lang: getPathnameLocale(url.pathname, this.#i18n),
|
|
1337
|
-
homeHref:
|
|
1338
|
-
pathname: url.pathname,
|
|
1339
|
-
i18n: this.#i18n,
|
|
1340
|
-
basePaths: this.#basePaths,
|
|
1341
|
-
headerBasePath: untrackedRequest()?.headers.get("x-base-path"),
|
|
1342
|
-
}),
|
|
1432
|
+
homeHref: this.#systemHomeHref(url),
|
|
1343
1433
|
stylesheetHref: this.#getStylesheetHref(url.pathname),
|
|
1344
1434
|
});
|
|
1345
1435
|
}
|
|
1346
1436
|
|
|
1437
|
+
static async #rootViewport(renderRootLayouts: RouteRender[]): Promise<RouteViewport | undefined> {
|
|
1438
|
+
const viewports = await Promise.all(renderRootLayouts.map((render) => render.getLayoutViewport?.()));
|
|
1439
|
+
return viewports.find((viewport) => viewport !== undefined);
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
#renderViewport(viewport: RouteViewport | undefined, theme?: string): ReactNode {
|
|
1443
|
+
return (
|
|
1444
|
+
<>
|
|
1445
|
+
<meta key="viewport" name="viewport" content={ViewportMeta.content(viewport)} />
|
|
1446
|
+
{ViewportMeta.themeColors(viewport, theme).map(({ content, media }) => (
|
|
1447
|
+
<meta key={`theme-color:${media ?? ""}`} name="theme-color" content={content} media={media} />
|
|
1448
|
+
))}
|
|
1449
|
+
</>
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
async #renderManifestLink(url: URL, renderRootLayouts: RouteRender[]): Promise<ReactNode> {
|
|
1454
|
+
const manifests = await Promise.all(renderRootLayouts.map((render) => render.getLayoutManifest?.()));
|
|
1455
|
+
if (!manifests.some((manifest) => manifest !== undefined)) return null;
|
|
1456
|
+
|
|
1457
|
+
const basePath = getBasePathFromPathname(url.pathname, { basePaths: this.#basePaths, i18n: this.#i18n });
|
|
1458
|
+
return <link key="manifest" rel="manifest" href={webManifestHref(basePath)} />;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1347
1461
|
#renderDefaultHead(): ReactNode {
|
|
1348
1462
|
return <title key="title">{process.env.AKAN_PUBLIC_APP_NAME ?? "Akan App"}</title>;
|
|
1349
1463
|
}
|
package/server/rscWorkerHost.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { ClientManifest } from "./artifact";
|
|
|
9
9
|
import type { RouteCacheInvalidation, RouteCacheRenderState } from "./cachePolicy";
|
|
10
10
|
import { ChildOutputReader } from "./logging/childOutputReader";
|
|
11
11
|
import { MemoryLimit } from "./memoryLimit";
|
|
12
|
-
import type { RscTraceMetadata,
|
|
12
|
+
import type { RscTraceMetadata, SsrLateControl } from "./ssrTypes";
|
|
13
13
|
import type { BaseBuildArtifact, CssAsset } from "./types";
|
|
14
14
|
|
|
15
15
|
const DEFAULT_RSC_HOST_MAX_PENDING_CHUNKS = 256;
|
|
@@ -23,6 +23,7 @@ export interface RscPending {
|
|
|
23
23
|
onCacheState?: (state: RouteCacheRenderState) => void;
|
|
24
24
|
onRedirect?: (location: string, method: RscRedirectMethod, status: RscRedirectStatus) => void;
|
|
25
25
|
onLateRedirect?: (location: string, method: RscRedirectMethod, status: RscRedirectStatus) => void;
|
|
26
|
+
onLateNotFound?: () => void;
|
|
26
27
|
onNotFound?: () => void;
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -48,7 +49,7 @@ export type RscRenderResult =
|
|
|
48
49
|
theme?: AkanTheme;
|
|
49
50
|
status?: number;
|
|
50
51
|
trace?: RscTraceMetadata;
|
|
51
|
-
lateControl: Promise<
|
|
52
|
+
lateControl: Promise<SsrLateControl | null>;
|
|
52
53
|
cacheState: Promise<RouteCacheRenderState>;
|
|
53
54
|
cancel: (reason?: unknown) => void;
|
|
54
55
|
}
|
|
@@ -159,9 +160,9 @@ export function createRscHostRenderStream(input: {
|
|
|
159
160
|
let theme: AkanTheme | undefined;
|
|
160
161
|
let status: number | undefined;
|
|
161
162
|
let trace: RscTraceMetadata | undefined;
|
|
162
|
-
let resolveLateControl!: (control:
|
|
163
|
+
let resolveLateControl!: (control: SsrLateControl | null) => void;
|
|
163
164
|
let resolveCacheState!: (state: RouteCacheRenderState) => void;
|
|
164
|
-
const lateControl = new Promise<
|
|
165
|
+
const lateControl = new Promise<SsrLateControl | null>((resolve) => {
|
|
165
166
|
resolveLateControl = resolve;
|
|
166
167
|
});
|
|
167
168
|
const cacheState = new Promise<RouteCacheRenderState>((resolve) => {
|
|
@@ -169,7 +170,7 @@ export function createRscHostRenderStream(input: {
|
|
|
169
170
|
});
|
|
170
171
|
let lateControlSettled = false;
|
|
171
172
|
let cacheStateSettled = false;
|
|
172
|
-
const settleLateControl = (control:
|
|
173
|
+
const settleLateControl = (control: SsrLateControl | null) => {
|
|
173
174
|
if (lateControlSettled) return;
|
|
174
175
|
lateControlSettled = true;
|
|
175
176
|
resolveLateControl(control);
|
|
@@ -277,6 +278,7 @@ export function createRscHostRenderStream(input: {
|
|
|
277
278
|
},
|
|
278
279
|
onLateRedirect: (location, method, status) =>
|
|
279
280
|
settleLateControl({ type: "redirect", location, method, status }),
|
|
281
|
+
onLateNotFound: () => settleLateControl({ type: "not-found" }),
|
|
280
282
|
onCacheState: settleCacheState,
|
|
281
283
|
onNotFound: () => {
|
|
282
284
|
settleLateControl(null);
|
|
@@ -326,11 +328,13 @@ type RscInMsg =
|
|
|
326
328
|
method?: RscRedirectMethod;
|
|
327
329
|
status?: RscRedirectStatus;
|
|
328
330
|
}
|
|
331
|
+
| { type: "late-not-found"; requestId: string }
|
|
329
332
|
| { type: "not-found"; requestId: string }
|
|
330
333
|
| { type: "metrics"; metrics: AkanMetricsReport }
|
|
331
334
|
| { type: "log.records"; records: LogRecord[]; dropped?: number }
|
|
332
335
|
| { type: "page-prompts.result"; requestId: string; result: PagePromptEntry[] }
|
|
333
336
|
| { type: "page-prompt.result"; requestId: string; result: PagePromptRun }
|
|
337
|
+
| { type: "web-manifest.result"; requestId: string; result: unknown }
|
|
334
338
|
| {
|
|
335
339
|
type: "error";
|
|
336
340
|
requestId: string;
|
|
@@ -475,6 +479,11 @@ export class RscWorker {
|
|
|
475
479
|
return this.#call<PagePromptRun>((requestId) => ({ type: "page-prompt.run", requestId, input }));
|
|
476
480
|
}
|
|
477
481
|
|
|
482
|
+
/** The root layout's `.manifest()` for `basePath`, in the browser's keys; `null` when it declares none. */
|
|
483
|
+
webManifest(basePath: string | null): Promise<unknown> {
|
|
484
|
+
return this.#call<unknown>((requestId) => ({ type: "web-manifest", requestId, basePath }));
|
|
485
|
+
}
|
|
486
|
+
|
|
478
487
|
#call<T>(message: (requestId: string) => object): Promise<T> {
|
|
479
488
|
const requestId = crypto.randomUUID();
|
|
480
489
|
return new Promise<T>((resolve, reject) => {
|
|
@@ -805,6 +814,9 @@ export class RscWorker {
|
|
|
805
814
|
.get(message.requestId)
|
|
806
815
|
?.onLateRedirect?.(message.location, message.method ?? "replace", message.status ?? 307);
|
|
807
816
|
return;
|
|
817
|
+
case "late-not-found":
|
|
818
|
+
this.#pending.get(message.requestId)?.onLateNotFound?.();
|
|
819
|
+
return;
|
|
808
820
|
case "not-found":
|
|
809
821
|
this.#resolvePending(message.requestId, (p) => p.onNotFound?.());
|
|
810
822
|
return;
|
|
@@ -817,6 +829,7 @@ export class RscWorker {
|
|
|
817
829
|
return;
|
|
818
830
|
case "page-prompts.result":
|
|
819
831
|
case "page-prompt.result":
|
|
832
|
+
case "web-manifest.result":
|
|
820
833
|
this.#settleCall(message.requestId, (call) => call.resolve(message.result));
|
|
821
834
|
return;
|
|
822
835
|
case "error":
|
package/server/sitemap.ts
CHANGED
|
@@ -34,6 +34,20 @@ export function isSitemapPath(pathname: string, basePaths: readonly string[]): b
|
|
|
34
34
|
return normalizePathname(pathname) === "/sitemap.xml";
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/** The basePath a `/manifest.webmanifest` request asks for: by a visible prefix, else by the request's basePath. */
|
|
38
|
+
export function getWebManifestBasePath(
|
|
39
|
+
pathname: string,
|
|
40
|
+
basePaths: readonly string[],
|
|
41
|
+
headerBasePath?: string | null,
|
|
42
|
+
): string | null | undefined {
|
|
43
|
+
const segments = normalizePathname(pathname).split("/").filter(Boolean);
|
|
44
|
+
if (segments.at(-1) !== "manifest.webmanifest" || segments.length > 2) return undefined;
|
|
45
|
+
const known = basePaths.map(normalizeSegment);
|
|
46
|
+
if (segments.length === 2) return known.includes(segments[0] ?? "") ? (segments[0] ?? null) : undefined;
|
|
47
|
+
const fromHeader = normalizeSegment(headerBasePath ?? "");
|
|
48
|
+
return known.includes(fromHeader) ? fromHeader : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
37
51
|
export function getSitemapBasePath(
|
|
38
52
|
pathname: string,
|
|
39
53
|
basePaths: readonly string[],
|
|
@@ -9,7 +9,13 @@ import { renderToReadableStream } from "react-dom/server.browser";
|
|
|
9
9
|
import { createFromNodeStream } from "react-server-dom-webpack/client.node";
|
|
10
10
|
import { parsePositiveInt } from "./cachePolicy";
|
|
11
11
|
import { concatBytes } from "./rscHttp";
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
SsrChunkRegistryStats,
|
|
14
|
+
SsrDocumentOptions,
|
|
15
|
+
SsrFromRscInput,
|
|
16
|
+
SsrLateControl,
|
|
17
|
+
SsrLateRedirect,
|
|
18
|
+
} from "./ssrTypes";
|
|
13
19
|
|
|
14
20
|
const DEFAULT_SSR_CHUNK_REGISTRY_MAX_ENTRIES = 1024;
|
|
15
21
|
const DEFAULT_MAX_PENDING_INLINE_RSC_SCRIPTS = 32;
|
|
@@ -196,7 +202,7 @@ export class ExpectedLateRedirectStderrSuppressor {
|
|
|
196
202
|
#lateRedirect = false;
|
|
197
203
|
#lateControlSettled = false;
|
|
198
204
|
|
|
199
|
-
private constructor(lateControl: Promise<
|
|
205
|
+
private constructor(lateControl: Promise<SsrLateControl | null>) {
|
|
200
206
|
lateControl
|
|
201
207
|
.then((control) => {
|
|
202
208
|
this.#lateRedirect = control?.type === "redirect";
|
|
@@ -210,7 +216,7 @@ export class ExpectedLateRedirectStderrSuppressor {
|
|
|
210
216
|
});
|
|
211
217
|
}
|
|
212
218
|
|
|
213
|
-
static start(lateControl?: Promise<
|
|
219
|
+
static start(lateControl?: Promise<SsrLateControl | null>): ExpectedLateRedirectStderrSuppressor | null {
|
|
214
220
|
if (!lateControl) return null;
|
|
215
221
|
|
|
216
222
|
if (process.env.NODE_ENV === "production" && process.env.AKAN_SUPPRESS_LATE_REDIRECT_STDERR !== "1") return null;
|
|
@@ -337,7 +343,7 @@ export function interleaveRscScriptsWithHtml(
|
|
|
337
343
|
rscClientStream: ReadableStream<Uint8Array>,
|
|
338
344
|
options: {
|
|
339
345
|
bootstrapModuleScripts?: string;
|
|
340
|
-
lateControl?: Promise<
|
|
346
|
+
lateControl?: Promise<SsrLateControl | null>;
|
|
341
347
|
maxPendingRscScripts?: number;
|
|
342
348
|
onPendingRscScriptsSize?: (size: number) => void;
|
|
343
349
|
onComplete?: () => void;
|
|
@@ -433,7 +439,7 @@ export function interleaveRscScriptsWithHtml(
|
|
|
433
439
|
const rscPump = pumpRscScripts();
|
|
434
440
|
const lateControlPump = options.lateControl?.then((control) => {
|
|
435
441
|
try {
|
|
436
|
-
if (
|
|
442
|
+
if (control?.type !== "redirect" || errored) return;
|
|
437
443
|
pendingControlScripts.push(encoder.encode(createSoftRedirectScript(control)));
|
|
438
444
|
notifyScriptAvailable();
|
|
439
445
|
} finally {
|
|
@@ -691,7 +697,7 @@ export class SsrFromRscRenderer {
|
|
|
691
697
|
|
|
692
698
|
static #suppressExpectedLateRedirectError(
|
|
693
699
|
thenable: PromiseLike<ReactNode>,
|
|
694
|
-
lateControl?: Promise<
|
|
700
|
+
lateControl?: Promise<SsrLateControl | null>,
|
|
695
701
|
): Promise<ReactNode> {
|
|
696
702
|
const promise = Promise.resolve(thenable);
|
|
697
703
|
if (!lateControl) return promise;
|
package/server/ssrTypes.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface SsrLateRedirect {
|
|
|
26
26
|
status: 303 | 307 | 308;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export type SsrLateControl = SsrLateRedirect | { type: "not-found" };
|
|
30
|
+
|
|
29
31
|
export interface RscTraceMetadata {
|
|
30
32
|
navId?: string;
|
|
31
33
|
pathname: string;
|
|
@@ -57,7 +59,7 @@ export interface SsrFromRscInput {
|
|
|
57
59
|
importmap?: Record<string, string>;
|
|
58
60
|
theme?: AkanTheme;
|
|
59
61
|
injectThemeInitScript?: boolean;
|
|
60
|
-
lateControl?: Promise<
|
|
62
|
+
lateControl?: Promise<SsrLateControl | null>;
|
|
61
63
|
onCancel?: (reason?: unknown) => void;
|
|
62
64
|
/** Buffers until `stream.allReady` (`pageConfig.ssr: "block"`); `AKAN_SSR_WAIT_FOR_ALL_READY=1` forces it globally. */
|
|
63
65
|
waitForAllReady?: boolean;
|
|
@@ -57,9 +57,45 @@ a { color: inherit; }
|
|
|
57
57
|
.akan-system-details { margin-top: 28px; max-height: 260px; overflow: auto; border-radius: 18px; background: rgba(26, 26, 26, 0.82); border: 1px solid rgba(240, 32, 32, 0.22); padding: 16px; color: rgba(255, 255, 255, 0.78); font-size: 0.82rem; line-height: 1.55; white-space: pre-wrap; }
|
|
58
58
|
`;
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
/** The card alone, styled by the app's own tokens: what a not-found raised after the layouts streamed renders in place. */
|
|
61
|
+
export const SystemPageMain = (options: Omit<SystemPageOptions, "lang" | "stylesheetHref" | "script">) => {
|
|
61
62
|
const copy = SYSTEM_PAGE_STATUS_COPY[options.kind];
|
|
62
63
|
const details = options.showDetails ? getSystemPageErrorDetails(options.error) : null;
|
|
64
|
+
return (
|
|
65
|
+
<main className="akan-system-page min-h-screen bg-background text-foreground">
|
|
66
|
+
<section
|
|
67
|
+
className="akan-system-card rounded-3xl border border-foreground/10 bg-foreground/4 p-8 shadow-2xl backdrop-blur-xl"
|
|
68
|
+
aria-labelledby="akan-system-title"
|
|
69
|
+
>
|
|
70
|
+
<p className="akan-system-status text-primary">{copy.status}</p>
|
|
71
|
+
<p className="akan-system-eyebrow text-primary">{copy.eyebrow}</p>
|
|
72
|
+
<h1 id="akan-system-title" className="akan-system-title font-black">
|
|
73
|
+
{copy.title}
|
|
74
|
+
</h1>
|
|
75
|
+
<p className="akan-system-description text-foreground/70">{copy.description}</p>
|
|
76
|
+
<p className="akan-system-path border border-foreground/10 bg-muted/50" aria-label="Requested path">
|
|
77
|
+
{options.pathname}
|
|
78
|
+
</p>
|
|
79
|
+
<div className="akan-system-actions">
|
|
80
|
+
<a className="akan-system-action" href={options.homeHref}>
|
|
81
|
+
{copy.actionLabel}
|
|
82
|
+
</a>
|
|
83
|
+
<a className="akan-system-action akan-system-secondary" href={options.pathname}>
|
|
84
|
+
Try again
|
|
85
|
+
</a>
|
|
86
|
+
</div>
|
|
87
|
+
{details ? (
|
|
88
|
+
<pre className="akan-system-details" aria-label="Development error details">
|
|
89
|
+
{details}
|
|
90
|
+
</pre>
|
|
91
|
+
) : null}
|
|
92
|
+
</section>
|
|
93
|
+
</main>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export function createSystemPageDocument(options: SystemPageOptions): ReactNode {
|
|
98
|
+
const copy = SYSTEM_PAGE_STATUS_COPY[options.kind];
|
|
63
99
|
const title = `${copy.status} - ${copy.eyebrow}`;
|
|
64
100
|
|
|
65
101
|
return (
|
|
@@ -75,35 +111,7 @@ export function createSystemPageDocument(options: SystemPageOptions): ReactNode
|
|
|
75
111
|
<style data-akan-system-page>{SYSTEM_PAGE_STYLE}</style>
|
|
76
112
|
</head>
|
|
77
113
|
<body>
|
|
78
|
-
<
|
|
79
|
-
<section
|
|
80
|
-
className="akan-system-card rounded-3xl border border-foreground/10 bg-foreground/4 p-8 shadow-2xl backdrop-blur-xl"
|
|
81
|
-
aria-labelledby="akan-system-title"
|
|
82
|
-
>
|
|
83
|
-
<p className="akan-system-status text-primary">{copy.status}</p>
|
|
84
|
-
<p className="akan-system-eyebrow text-primary">{copy.eyebrow}</p>
|
|
85
|
-
<h1 id="akan-system-title" className="akan-system-title font-black">
|
|
86
|
-
{copy.title}
|
|
87
|
-
</h1>
|
|
88
|
-
<p className="akan-system-description text-foreground/70">{copy.description}</p>
|
|
89
|
-
<p className="akan-system-path border border-foreground/10 bg-muted/50" aria-label="Requested path">
|
|
90
|
-
{options.pathname}
|
|
91
|
-
</p>
|
|
92
|
-
<div className="akan-system-actions">
|
|
93
|
-
<a className="akan-system-action" href={options.homeHref}>
|
|
94
|
-
{copy.actionLabel}
|
|
95
|
-
</a>
|
|
96
|
-
<a className="akan-system-action akan-system-secondary" href={options.pathname}>
|
|
97
|
-
Try again
|
|
98
|
-
</a>
|
|
99
|
-
</div>
|
|
100
|
-
{details ? (
|
|
101
|
-
<pre className="akan-system-details" aria-label="Development error details">
|
|
102
|
-
{details}
|
|
103
|
-
</pre>
|
|
104
|
-
) : null}
|
|
105
|
-
</section>
|
|
106
|
-
</main>
|
|
114
|
+
<SystemPageMain {...options} />
|
|
107
115
|
{options.script ? (
|
|
108
116
|
|
|
109
117
|
<script dangerouslySetInnerHTML={{ __html: options.script }} />
|
package/server/types.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AkanWebConfig } from "akanjs";
|
|
2
2
|
import type { PromiseOrObject } from "akanjs/base";
|
|
3
3
|
import type { AkanI18nConfig } from "akanjs/common";
|
|
4
|
+
import type { WebsocketRoomCursor } from "akanjs/signal";
|
|
4
5
|
import type { ClientManifest } from "./artifact";
|
|
5
6
|
import type { SsrManifest } from "./ssrTypes";
|
|
6
7
|
|
|
@@ -8,6 +9,7 @@ export type WebsocketRoute = (
|
|
|
8
9
|
ws: Bun.ServerWebSocket<unknown>,
|
|
9
10
|
data: unknown[],
|
|
10
11
|
event?: "message" | "subscribe" | "unsubscribe",
|
|
12
|
+
request?: { since?: WebsocketRoomCursor },
|
|
11
13
|
) => PromiseOrObject<unknown>;
|
|
12
14
|
|
|
13
15
|
export type WebsocketRoutes = Record<string, WebsocketRoute>;
|