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/ui/overlayPosition.ts
CHANGED
|
@@ -89,3 +89,93 @@ export const useOverlayPosition = ({
|
|
|
89
89
|
|
|
90
90
|
return position;
|
|
91
91
|
};
|
|
92
|
+
|
|
93
|
+
export type OverlaySide = "top" | "right" | "bottom" | "left";
|
|
94
|
+
|
|
95
|
+
export interface AnchoredPosition {
|
|
96
|
+
top: number;
|
|
97
|
+
left: number;
|
|
98
|
+
/** The side the panel landed on: the asked one, or its opposite when that has more room. */
|
|
99
|
+
side: OverlaySide;
|
|
100
|
+
/** The trigger's centre along the panel's edge, held clear of its corners, for the pointer. */
|
|
101
|
+
arrowOffset: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const oppositeSide = { top: "bottom", bottom: "top", left: "right", right: "left" } as const;
|
|
105
|
+
|
|
106
|
+
/** Like {@link useOverlayPosition}, for a panel centred on any side of its trigger, as a tooltip is. */
|
|
107
|
+
export const useAnchoredPosition = ({
|
|
108
|
+
opened,
|
|
109
|
+
triggerRef,
|
|
110
|
+
panelRef,
|
|
111
|
+
side,
|
|
112
|
+
gap = TRIGGER_GAP + 2,
|
|
113
|
+
}: {
|
|
114
|
+
opened: boolean;
|
|
115
|
+
triggerRef: RefObject<HTMLElement | null>;
|
|
116
|
+
panelRef: RefObject<HTMLElement | null>;
|
|
117
|
+
side: OverlaySide;
|
|
118
|
+
gap?: number;
|
|
119
|
+
}) => {
|
|
120
|
+
const [position, setPosition] = useState<AnchoredPosition | null>(null);
|
|
121
|
+
|
|
122
|
+
const measure = useCallback(() => {
|
|
123
|
+
const trigger = triggerRef.current?.getBoundingClientRect();
|
|
124
|
+
const panel = panelRef.current;
|
|
125
|
+
if (!trigger || !panel) return;
|
|
126
|
+
const { offsetHeight: height, offsetWidth: width } = panel;
|
|
127
|
+
const room = {
|
|
128
|
+
top: trigger.top - gap - VIEWPORT_MARGIN,
|
|
129
|
+
bottom: window.innerHeight - trigger.bottom - gap - VIEWPORT_MARGIN,
|
|
130
|
+
left: trigger.left - gap - VIEWPORT_MARGIN,
|
|
131
|
+
right: window.innerWidth - trigger.right - gap - VIEWPORT_MARGIN,
|
|
132
|
+
};
|
|
133
|
+
const vertical = side === "top" || side === "bottom";
|
|
134
|
+
const need = vertical ? height : width;
|
|
135
|
+
const landed = room[side] >= need || room[side] >= room[oppositeSide[side]] ? side : oppositeSide[side];
|
|
136
|
+
const centerX = (trigger.left + trigger.right) / 2;
|
|
137
|
+
const centerY = (trigger.top + trigger.bottom) / 2;
|
|
138
|
+
const next: AnchoredPosition = vertical
|
|
139
|
+
? {
|
|
140
|
+
top: landed === "top" ? trigger.top - gap - height : trigger.bottom + gap,
|
|
141
|
+
left: clamp(centerX - width / 2, VIEWPORT_MARGIN, window.innerWidth - VIEWPORT_MARGIN - width),
|
|
142
|
+
side: landed,
|
|
143
|
+
arrowOffset: 0,
|
|
144
|
+
}
|
|
145
|
+
: {
|
|
146
|
+
top: clamp(centerY - height / 2, VIEWPORT_MARGIN, window.innerHeight - VIEWPORT_MARGIN - height),
|
|
147
|
+
left: landed === "left" ? trigger.left - gap - width : trigger.right + gap,
|
|
148
|
+
side: landed,
|
|
149
|
+
arrowOffset: 0,
|
|
150
|
+
};
|
|
151
|
+
next.arrowOffset = vertical
|
|
152
|
+
? clamp(centerX - next.left, POINTER_INSET / 2, width - POINTER_INSET / 2)
|
|
153
|
+
: clamp(centerY - next.top, POINTER_INSET / 2, height - POINTER_INSET / 2);
|
|
154
|
+
setPosition((prev) =>
|
|
155
|
+
prev &&
|
|
156
|
+
prev.top === next.top &&
|
|
157
|
+
prev.left === next.left &&
|
|
158
|
+
prev.side === next.side &&
|
|
159
|
+
prev.arrowOffset === next.arrowOffset
|
|
160
|
+
? prev
|
|
161
|
+
: next,
|
|
162
|
+
);
|
|
163
|
+
}, [gap, panelRef, side, triggerRef]);
|
|
164
|
+
|
|
165
|
+
useLayoutEffect(() => {
|
|
166
|
+
if (opened) measure();
|
|
167
|
+
else setPosition(null);
|
|
168
|
+
}, [opened, measure]);
|
|
169
|
+
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
if (!opened) return;
|
|
172
|
+
window.addEventListener("scroll", measure, true);
|
|
173
|
+
window.addEventListener("resize", measure);
|
|
174
|
+
return () => {
|
|
175
|
+
window.removeEventListener("scroll", measure, true);
|
|
176
|
+
window.removeEventListener("resize", measure);
|
|
177
|
+
};
|
|
178
|
+
}, [opened, measure]);
|
|
179
|
+
|
|
180
|
+
return position;
|
|
181
|
+
};
|
package/ui/recipe/inputRecipe.ts
CHANGED
|
@@ -12,7 +12,14 @@ export const inputRecipe = recipe(
|
|
|
12
12
|
select:
|
|
13
13
|
"appearance-none bg-[image:linear-gradient(to_top_right,transparent_50%,currentColor_50%,currentColor_calc(50%+2px),transparent_calc(50%+2px)),linear-gradient(to_bottom_right,transparent_calc(50%-2px),currentColor_calc(50%-2px),currentColor_50%,transparent_50%)] bg-[length:5px_6px] bg-[position:calc(100%-14px)_50%,calc(100%-9px)_50%] bg-no-repeat pr-7 pl-3",
|
|
14
14
|
},
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
size: {
|
|
17
|
+
xs: "text-xs pointer-coarse:text-base",
|
|
18
|
+
sm: "text-sm pointer-coarse:text-base",
|
|
19
|
+
md: "text-sm pointer-coarse:text-base",
|
|
20
|
+
lg: "text-base",
|
|
21
|
+
xl: "text-lg",
|
|
22
|
+
},
|
|
16
23
|
tone: {
|
|
17
24
|
default: "",
|
|
18
25
|
primary: "border-primary",
|
package/ui/styles.css
CHANGED
|
@@ -274,6 +274,158 @@ body.akan-mobile-document {
|
|
|
274
274
|
padding-inline: 2rem;
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
+
/* A frame whose insets change with width carries one var per breakpoint (`topInset: { base, md }`, SSR only);
|
|
278
|
+
these pick the one in force. The breakpoints are Tailwind's defaults, which `ResponsiveInset` keys on. */
|
|
279
|
+
[data-akan-frame-responsive] {
|
|
280
|
+
--akan-top-inset: var(--akan-top-inset-base);
|
|
281
|
+
--akan-bottom-inset: var(--akan-bottom-inset-base);
|
|
282
|
+
--akan-top-inset-visibility: var(--akan-top-inset-visibility-base);
|
|
283
|
+
--akan-bottom-inset-visibility: var(--akan-bottom-inset-visibility-base);
|
|
284
|
+
}
|
|
285
|
+
@media (width >= 40rem) {
|
|
286
|
+
[data-akan-frame-responsive] {
|
|
287
|
+
--akan-top-inset: var(--akan-top-inset-sm);
|
|
288
|
+
--akan-bottom-inset: var(--akan-bottom-inset-sm);
|
|
289
|
+
--akan-top-inset-visibility: var(--akan-top-inset-visibility-sm);
|
|
290
|
+
--akan-bottom-inset-visibility: var(--akan-bottom-inset-visibility-sm);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
@media (width >= 48rem) {
|
|
294
|
+
[data-akan-frame-responsive] {
|
|
295
|
+
--akan-top-inset: var(--akan-top-inset-md);
|
|
296
|
+
--akan-bottom-inset: var(--akan-bottom-inset-md);
|
|
297
|
+
--akan-top-inset-visibility: var(--akan-top-inset-visibility-md);
|
|
298
|
+
--akan-bottom-inset-visibility: var(--akan-bottom-inset-visibility-md);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
@media (width >= 64rem) {
|
|
302
|
+
[data-akan-frame-responsive] {
|
|
303
|
+
--akan-top-inset: var(--akan-top-inset-lg);
|
|
304
|
+
--akan-bottom-inset: var(--akan-bottom-inset-lg);
|
|
305
|
+
--akan-top-inset-visibility: var(--akan-top-inset-visibility-lg);
|
|
306
|
+
--akan-bottom-inset-visibility: var(--akan-bottom-inset-visibility-lg);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
@media (width >= 80rem) {
|
|
310
|
+
[data-akan-frame-responsive] {
|
|
311
|
+
--akan-top-inset: var(--akan-top-inset-xl);
|
|
312
|
+
--akan-bottom-inset: var(--akan-bottom-inset-xl);
|
|
313
|
+
--akan-top-inset-visibility: var(--akan-top-inset-visibility-xl);
|
|
314
|
+
--akan-bottom-inset-visibility: var(--akan-bottom-inset-visibility-xl);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
@media (width >= 96rem) {
|
|
318
|
+
[data-akan-frame-responsive] {
|
|
319
|
+
--akan-top-inset: var(--akan-top-inset-2xl);
|
|
320
|
+
--akan-bottom-inset: var(--akan-bottom-inset-2xl);
|
|
321
|
+
--akan-top-inset-visibility: var(--akan-top-inset-visibility-2xl);
|
|
322
|
+
--akan-bottom-inset-visibility: var(--akan-bottom-inset-visibility-2xl);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* Route transitions on the web: an RSC navigation tags itself `akan-<transition>-<push|pop>` from the route's
|
|
327
|
+
`.config({ transition })`, and the page content's ViewTransition takes that class. The rest of the page holds. */
|
|
328
|
+
:root:active-view-transition-type(akan-nav)::view-transition-old(root),
|
|
329
|
+
:root:active-view-transition-type(akan-nav)::view-transition-new(root) {
|
|
330
|
+
animation: none;
|
|
331
|
+
}
|
|
332
|
+
::view-transition-old(.akan-stack-push) {
|
|
333
|
+
animation: 260ms cubic-bezier(0.32, 0.72, 0, 1) both akan-vt-out-left;
|
|
334
|
+
}
|
|
335
|
+
::view-transition-new(.akan-stack-push) {
|
|
336
|
+
animation: 260ms cubic-bezier(0.32, 0.72, 0, 1) both akan-vt-in-right;
|
|
337
|
+
}
|
|
338
|
+
::view-transition-old(.akan-stack-pop) {
|
|
339
|
+
animation: 260ms cubic-bezier(0.32, 0.72, 0, 1) both akan-vt-out-right;
|
|
340
|
+
}
|
|
341
|
+
::view-transition-new(.akan-stack-pop) {
|
|
342
|
+
animation: 260ms cubic-bezier(0.32, 0.72, 0, 1) both akan-vt-in-left;
|
|
343
|
+
}
|
|
344
|
+
::view-transition-old(.akan-bottomUp-push),
|
|
345
|
+
::view-transition-new(.akan-bottomUp-pop) {
|
|
346
|
+
animation: none;
|
|
347
|
+
}
|
|
348
|
+
::view-transition-new(.akan-bottomUp-push) {
|
|
349
|
+
animation: 280ms cubic-bezier(0.32, 0.72, 0, 1) both akan-vt-in-up;
|
|
350
|
+
}
|
|
351
|
+
::view-transition-old(.akan-bottomUp-pop) {
|
|
352
|
+
animation: 240ms ease-in both akan-vt-out-down;
|
|
353
|
+
}
|
|
354
|
+
::view-transition-old(.akan-fade-push),
|
|
355
|
+
::view-transition-old(.akan-fade-pop) {
|
|
356
|
+
animation: 180ms ease-in both akan-vt-fade-out;
|
|
357
|
+
}
|
|
358
|
+
::view-transition-new(.akan-fade-push),
|
|
359
|
+
::view-transition-new(.akan-fade-pop) {
|
|
360
|
+
animation: 180ms ease-out both akan-vt-fade-in;
|
|
361
|
+
}
|
|
362
|
+
::view-transition-old(.akan-scaleOut-push) {
|
|
363
|
+
animation: 220ms ease-in both akan-vt-shrink-out;
|
|
364
|
+
}
|
|
365
|
+
::view-transition-new(.akan-scaleOut-push) {
|
|
366
|
+
animation: 220ms ease-out both akan-vt-fade-in;
|
|
367
|
+
}
|
|
368
|
+
::view-transition-old(.akan-scaleOut-pop) {
|
|
369
|
+
animation: 200ms ease-in both akan-vt-fade-out;
|
|
370
|
+
}
|
|
371
|
+
::view-transition-new(.akan-scaleOut-pop) {
|
|
372
|
+
animation: 220ms ease-out both akan-vt-grow-in;
|
|
373
|
+
}
|
|
374
|
+
@keyframes akan-vt-in-right {
|
|
375
|
+
from {
|
|
376
|
+
transform: translateX(100%);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
@keyframes akan-vt-out-left {
|
|
380
|
+
to {
|
|
381
|
+
transform: translateX(-30%);
|
|
382
|
+
opacity: 0.6;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
@keyframes akan-vt-in-left {
|
|
386
|
+
from {
|
|
387
|
+
transform: translateX(-30%);
|
|
388
|
+
opacity: 0.6;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
@keyframes akan-vt-out-right {
|
|
392
|
+
to {
|
|
393
|
+
transform: translateX(100%);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
@keyframes akan-vt-in-up {
|
|
397
|
+
from {
|
|
398
|
+
transform: translateY(100%);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
@keyframes akan-vt-out-down {
|
|
402
|
+
to {
|
|
403
|
+
transform: translateY(100%);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
@keyframes akan-vt-fade-in {
|
|
407
|
+
from {
|
|
408
|
+
opacity: 0;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
@keyframes akan-vt-fade-out {
|
|
412
|
+
to {
|
|
413
|
+
opacity: 0;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
@keyframes akan-vt-shrink-out {
|
|
417
|
+
to {
|
|
418
|
+
transform: scale(0.94);
|
|
419
|
+
opacity: 0;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
@keyframes akan-vt-grow-in {
|
|
423
|
+
from {
|
|
424
|
+
transform: scale(0.94);
|
|
425
|
+
opacity: 0;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
277
429
|
/* Always-included keyframes (non-theme animations) */
|
|
278
430
|
@keyframes smaller {
|
|
279
431
|
0% {
|
|
@@ -221,6 +221,11 @@ class AkanNativeActivity : Activity(), AkanNativePluginContext.Host {
|
|
|
221
221
|
if (debuggable && intent.hasExtra(EXTRA_ENV)) webView.reload()
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
override fun onResume() {
|
|
225
|
+
super.onResume()
|
|
226
|
+
if (AkanNativeFeatures.UPDATES && !unsupportedWebView) AkanNativeUpdateReports.flush()
|
|
227
|
+
}
|
|
228
|
+
|
|
224
229
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
225
230
|
super.onConfigurationChanged(newConfig)
|
|
226
231
|
if (unsupportedWebView) return
|
|
@@ -373,10 +378,14 @@ class AkanNativeActivity : Activity(), AkanNativePluginContext.Host {
|
|
|
373
378
|
}
|
|
374
379
|
}
|
|
375
380
|
|
|
381
|
+
//? Armed when the page starts, not when it finishes: a bundle stuck in a script never finishes loading.
|
|
382
|
+
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
|
|
383
|
+
startReadyTimer()
|
|
384
|
+
}
|
|
385
|
+
|
|
376
386
|
override fun onPageFinished(view: WebView, url: String) {
|
|
377
387
|
root.requestApplyInsets() // push insets into the new document
|
|
378
388
|
if (splashConfig.optBoolean("autoHide", true)) hideSplash(SPLASH_FADE_MS)
|
|
379
|
-
startReadyTimer()
|
|
380
389
|
}
|
|
381
390
|
|
|
382
391
|
override fun doUpdateVisitedHistory(view: WebView, url: String, isReload: Boolean) {
|
|
@@ -841,7 +850,16 @@ class AkanNativeActivity : Activity(), AkanNativePluginContext.Host {
|
|
|
841
850
|
// ---------------------------------------------------------------- web bundle updates (UP-2)
|
|
842
851
|
|
|
843
852
|
private val readyTimeout = Runnable {
|
|
844
|
-
if (AkanNativeFeatures.UPDATES
|
|
853
|
+
if (!AkanNativeFeatures.UPDATES || !AkanNativeUpdates.onTrial) return@Runnable
|
|
854
|
+
val (root, trial) = AkanNativeUpdates.rollback()
|
|
855
|
+
// A renderer stuck in a script never runs the next navigation: one that does not answer is ended, and
|
|
856
|
+
// onRenderProcessGone makes the activity again, on the bundle the rollback made active.
|
|
857
|
+
var answered = false
|
|
858
|
+
webView.evaluateJavascript("0") { answered = true }
|
|
859
|
+
webView.postDelayed({
|
|
860
|
+
trial?.let { AkanNativeUpdateReports.record("rolledBack", it, if (answered) "timeout" else "hang") }
|
|
861
|
+
if (answered || webView.webViewRenderProcess?.terminate() != true) serveBundle(root)
|
|
862
|
+
}, RENDERER_PROBE_MS)
|
|
845
863
|
}
|
|
846
864
|
|
|
847
865
|
/** A bundle on trial must call notifyReady() within updates.readyTimeout of loading. */
|
|
@@ -977,6 +995,8 @@ class AkanNativeActivity : Activity(), AkanNativePluginContext.Host {
|
|
|
977
995
|
const val EXTRA_ENV = "akanNativeEnv"
|
|
978
996
|
private const val STATE_PENDING = "akan-native.pendingResults"
|
|
979
997
|
private const val SPLASH_FADE_MS = 200L
|
|
998
|
+
/** How long a page on trial has to answer before its renderer counts as stuck. */
|
|
999
|
+
private const val RENDERER_PROBE_MS = 2_000L
|
|
980
1000
|
// Must match @akanjs/native/core protocol.ts INIT_PREFIX / INIT_SUFFIX.
|
|
981
1001
|
private const val INIT_PREFIX = "window.__AKAN_NATIVE__=(function(o,b,e){for(var k in b)o[k]=b[k];o.env=e;return o})(window.__AKAN_NATIVE__||{},"
|
|
982
1002
|
private const val INIT_SUFFIX = ");\n"
|
|
@@ -3,6 +3,7 @@ package com.akanjs.runtime
|
|
|
3
3
|
import java.nio.ByteBuffer
|
|
4
4
|
import java.nio.charset.CharacterCodingException
|
|
5
5
|
import java.nio.charset.CodingErrorAction
|
|
6
|
+
import java.security.MessageDigest
|
|
6
7
|
|
|
7
8
|
/*
|
|
8
9
|
* Decisions every akan-native host makes the same way (docs/architecture.md §5): a port of
|
|
@@ -21,6 +22,13 @@ sealed class AkanNativeRoute {
|
|
|
21
22
|
object NotFound : AkanNativeRoute()
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
/** kernel.ts UpdateDecision: what a verified web bundle manifest means to this binary (updates protocol v2). */
|
|
26
|
+
data class AkanNativeUpdateDecision(val kind: Kind, val baseline: Double = 0.0, val revoke: List<String> = emptyList()) {
|
|
27
|
+
enum class Kind(val wire: String) { UNTRUSTED("untrusted"), REVOKED("revoked"), SELF_REVOKE("self-revoke"), REVOKE_LATER("revoke-later"), TAKE("take"), STALE("stale") }
|
|
28
|
+
|
|
29
|
+
val wire: String get() = if (kind == Kind.TAKE || kind == Kind.STALE) "${kind.wire}:${baseline.toLong()}:${revoke.joinToString(",")}" else kind.wire
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
sealed class AkanNativeRangeAnswer {
|
|
25
33
|
object Whole : AkanNativeRangeAnswer()
|
|
26
34
|
/** First and last byte, inclusive. */
|
|
@@ -321,4 +329,33 @@ object AkanNativeKernel {
|
|
|
321
329
|
if (!topLevel) return if (scheme in AkanNativeContract.FRAME_SCHEMES) AkanNativeNavigation.LOAD else AkanNativeNavigation.DROP
|
|
322
330
|
return if (scheme in externalSchemes(extra)) AkanNativeNavigation.OPEN else AkanNativeNavigation.DROP
|
|
323
331
|
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* kernel.ts updateDecision: a key revokes only keys listed before it (a leaked online key, listed first, cannot
|
|
335
|
+
* revoke the spares that recover from it), and resetSequence counts only when it newly revokes a key.
|
|
336
|
+
*/
|
|
337
|
+
fun updateDecision(
|
|
338
|
+
ring: List<String>, revoked: List<String>, signer: String, revokeKeys: List<String>, resetSequence: Boolean,
|
|
339
|
+
sequence: Double, embedded: Double, running: Pair<Double, String>?,
|
|
340
|
+
): AkanNativeUpdateDecision {
|
|
341
|
+
val index = ring.indexOf(signer)
|
|
342
|
+
if (index < 0) return AkanNativeUpdateDecision(AkanNativeUpdateDecision.Kind.UNTRUSTED)
|
|
343
|
+
if (signer in revoked) return AkanNativeUpdateDecision(AkanNativeUpdateDecision.Kind.REVOKED)
|
|
344
|
+
if (signer in revokeKeys) return AkanNativeUpdateDecision(AkanNativeUpdateDecision.Kind.SELF_REVOKE)
|
|
345
|
+
if (revokeKeys.any { ring.indexOf(it) > index }) return AkanNativeUpdateDecision(AkanNativeUpdateDecision.Kind.REVOKE_LATER)
|
|
346
|
+
val revoke = ring.filter { it in revokeKeys && it !in revoked }
|
|
347
|
+
val bar = running?.takeIf { it.second !in revoked && it.second !in revoke }?.first ?: 0.0
|
|
348
|
+
val baseline = if (resetSequence && revoke.isNotEmpty()) embedded else maxOf(embedded, bar)
|
|
349
|
+
val kind = if (sequence > baseline) AkanNativeUpdateDecision.Kind.TAKE else AkanNativeUpdateDecision.Kind.STALE
|
|
350
|
+
return AkanNativeUpdateDecision(kind, baseline, revoke)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** kernel.ts updateManifestUrl: one envelope per channel and native API. */
|
|
354
|
+
fun updateManifestUrl(url: String, platform: String, channel: String, nativeApi: String): String = "$url/$platform/$channel/$nativeApi.json"
|
|
355
|
+
|
|
356
|
+
/** The first 8 bytes of the raw public key's SHA-256, in hex: what an envelope names its signer by. */
|
|
357
|
+
fun updateKeyId(publicKey: ByteArray): String =
|
|
358
|
+
MessageDigest.getInstance("SHA-256").digest(publicKey).take(8).joinToString("") { "%02x".format(it) }
|
|
359
|
+
|
|
360
|
+
fun isUpdateKeyId(s: String): Boolean = s.length == 16 && s.all { it in '0'..'9' || it in 'a'..'f' }
|
|
324
361
|
}
|
package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdateReports.kt
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// What became of each web bundle release (F-S2), posted to updates.report.url: the Android twin of
|
|
2
|
+
// native/ios/Sources/AkanNativeUpdateReports.swift, same file and wire format.
|
|
3
|
+
//
|
|
4
|
+
// noBackupFilesDir/akan-native-updates/report.json: { installId, nextId, events: [{ id, event, bundle, sequence, reason,
|
|
5
|
+
// at }], retryAt?, seen: [key] }. reset() rewrites state.json only, so the installId (random, not a device identifier)
|
|
6
|
+
// and the id counter live as long as the install, and an id is never used twice.
|
|
7
|
+
// Sent from the activity's onResume, so at launch and on each return to the foreground: oldest first, 20 to a request,
|
|
8
|
+
// one request at a time. A 2xx removes the batch; a 4xx other than 408 and 429 drops it, since the same body would be
|
|
9
|
+
// refused forever; a 408 (a body slower than the server waits for), a 429, a 5xx or no answer keeps it for the next time
|
|
10
|
+
// (after Retry-After). At most 100 wait, the oldest dropped first. Main thread only; the request runs on a worker thread.
|
|
11
|
+
package com.akanjs.runtime
|
|
12
|
+
|
|
13
|
+
import android.os.Build
|
|
14
|
+
import android.os.Handler
|
|
15
|
+
import android.os.Looper
|
|
16
|
+
import android.util.AtomicFile
|
|
17
|
+
import android.util.Log
|
|
18
|
+
import java.io.File
|
|
19
|
+
import java.io.IOException
|
|
20
|
+
import java.net.HttpURLConnection
|
|
21
|
+
import java.net.URL
|
|
22
|
+
import java.security.SecureRandom
|
|
23
|
+
import java.time.Instant
|
|
24
|
+
import java.time.temporal.ChronoUnit
|
|
25
|
+
import org.json.JSONArray
|
|
26
|
+
import org.json.JSONObject
|
|
27
|
+
|
|
28
|
+
object AkanNativeUpdateReports {
|
|
29
|
+
private const val TAG = "AkanNativeUpdates"
|
|
30
|
+
private const val LIMIT = 100
|
|
31
|
+
private const val BATCH = 20
|
|
32
|
+
private val main = Handler(Looper.getMainLooper())
|
|
33
|
+
private var sending = false
|
|
34
|
+
|
|
35
|
+
private class Report(
|
|
36
|
+
val installId: String,
|
|
37
|
+
var nextId: Long,
|
|
38
|
+
val events: MutableList<JSONObject>,
|
|
39
|
+
var retryAt: Double?,
|
|
40
|
+
val seen: MutableList<String>,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
private val file get() = AtomicFile(File(AkanNativeUpdates.directory, "report.json"))
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* downloaded, applied, confirmed, rolledBack (timeout, relaunch, hang, verify) or failed (download, verify, disk,
|
|
47
|
+
* signature, manifest). Nothing is kept when the build names no report URL.
|
|
48
|
+
*/
|
|
49
|
+
fun record(event: String, bundle: String?, sequence: Double?, reason: String? = null) {
|
|
50
|
+
if (AkanNativeUpdates.config?.reportUrl == null || bundle == null || !AkanNativeKernel.isBundleId(bundle)) return
|
|
51
|
+
val report = read()
|
|
52
|
+
// A check runs at every launch: the same failure of the same release is one event, not one per launch.
|
|
53
|
+
val key = "$event|$bundle|${reason ?: ""}"
|
|
54
|
+
if (key in report.seen) return
|
|
55
|
+
report.seen.add(key)
|
|
56
|
+
while (report.seen.size > LIMIT) report.seen.removeAt(0)
|
|
57
|
+
val number: Any = sequence?.let { if (it == Math.rint(it) && Math.abs(it) < 9e15) it.toLong() else it } ?: JSONObject.NULL
|
|
58
|
+
report.events.add(
|
|
59
|
+
JSONObject().put("id", report.nextId).put("event", event).put("bundle", bundle).put("sequence", number)
|
|
60
|
+
.put("reason", reason ?: JSONObject.NULL).put("at", Instant.now().truncatedTo(ChronoUnit.MILLIS).toString()),
|
|
61
|
+
)
|
|
62
|
+
while (report.events.size > LIMIT) report.events.removeAt(0)
|
|
63
|
+
report.nextId++
|
|
64
|
+
write(report)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fun record(event: String, entry: AkanNativeUpdateEntry, reason: String? = null) = record(event, entry.bundle, entry.sequence, reason)
|
|
68
|
+
|
|
69
|
+
fun flush() {
|
|
70
|
+
val config = AkanNativeUpdates.config ?: return
|
|
71
|
+
val address = config.reportUrl ?: return
|
|
72
|
+
if (sending) return
|
|
73
|
+
val report = read()
|
|
74
|
+
val now = System.currentTimeMillis() / 1000.0
|
|
75
|
+
// A Retry-After more than a day out is a clock that moved, not the server's wish.
|
|
76
|
+
report.retryAt?.let { if (it > now && it < now + 86_400) return }
|
|
77
|
+
val events = report.events.take(BATCH)
|
|
78
|
+
if (events.isEmpty()) return
|
|
79
|
+
val body = JSONObject()
|
|
80
|
+
.put("app", config.app).put("platform", config.platform).put("channel", config.channel).put("installId", report.installId)
|
|
81
|
+
.put("nativeApi", config.nativeApi).put("appVersion", config.version).put("os", "Android ${Build.VERSION.RELEASE}")
|
|
82
|
+
.put("events", JSONArray(events))
|
|
83
|
+
.toString().toByteArray()
|
|
84
|
+
val ids = events.map { it.optLong("id") }.toSet()
|
|
85
|
+
sending = true
|
|
86
|
+
Thread {
|
|
87
|
+
var status = 0
|
|
88
|
+
var retryAfter: String? = null
|
|
89
|
+
try {
|
|
90
|
+
val connection = URL(address).openConnection() as HttpURLConnection
|
|
91
|
+
try {
|
|
92
|
+
connection.requestMethod = "POST"
|
|
93
|
+
connection.doOutput = true
|
|
94
|
+
connection.connectTimeout = 30_000
|
|
95
|
+
connection.readTimeout = 30_000
|
|
96
|
+
connection.useCaches = false
|
|
97
|
+
connection.setRequestProperty("Content-Type", "application/json")
|
|
98
|
+
connection.setFixedLengthStreamingMode(body.size)
|
|
99
|
+
connection.outputStream.use { it.write(body) }
|
|
100
|
+
status = connection.responseCode
|
|
101
|
+
retryAfter = connection.getHeaderField("Retry-After")
|
|
102
|
+
} finally {
|
|
103
|
+
connection.disconnect()
|
|
104
|
+
}
|
|
105
|
+
} catch (e: Exception) {
|
|
106
|
+
Log.w(TAG, "cannot post update reports: $e")
|
|
107
|
+
}
|
|
108
|
+
main.post {
|
|
109
|
+
sending = false
|
|
110
|
+
settle(ids, status, retryAfter)
|
|
111
|
+
}
|
|
112
|
+
}.start()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private fun settle(ids: Set<Long>, status: Int, retryAfter: String?) {
|
|
116
|
+
val report = read()
|
|
117
|
+
if (status !in 200..299 && (status !in 400..499 || status == 408 || status == 429)) {
|
|
118
|
+
report.retryAt = retryAfter?.trim()?.toDoubleOrNull()?.let { System.currentTimeMillis() / 1000.0 + it }
|
|
119
|
+
write(report)
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
report.events.removeAll { it.optLong("id") in ids }
|
|
123
|
+
report.retryAt = null
|
|
124
|
+
write(report)
|
|
125
|
+
flush()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private fun read(): Report {
|
|
129
|
+
val o = try {
|
|
130
|
+
JSONObject(String(file.readFully()))
|
|
131
|
+
} catch (e: Exception) {
|
|
132
|
+
JSONObject()
|
|
133
|
+
}
|
|
134
|
+
val installId = o.optString("installId")
|
|
135
|
+
if (!Regex("[0-9a-fA-F]{32}").matches(installId)) return Report(newInstallId(), 0, mutableListOf(), null, mutableListOf())
|
|
136
|
+
val events = o.optJSONArray("events")
|
|
137
|
+
val seen = o.optJSONArray("seen")
|
|
138
|
+
return Report(
|
|
139
|
+
installId,
|
|
140
|
+
o.optLong("nextId"),
|
|
141
|
+
MutableList(events?.length() ?: 0) { events?.optJSONObject(it) }.filterNotNull().toMutableList(),
|
|
142
|
+
if (o.has("retryAt")) o.optDouble("retryAt") else null,
|
|
143
|
+
MutableList(seen?.length() ?: 0) { seen?.optString(it).orEmpty() }.filter { it.isNotEmpty() }.toMutableList(),
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private fun write(report: Report) {
|
|
148
|
+
AkanNativeUpdates.directory.mkdirs()
|
|
149
|
+
val o = JSONObject().put("installId", report.installId).put("nextId", report.nextId).put("events", JSONArray(report.events))
|
|
150
|
+
.put("seen", JSONArray(report.seen))
|
|
151
|
+
report.retryAt?.let { o.put("retryAt", it) }
|
|
152
|
+
val target = file
|
|
153
|
+
val out = try {
|
|
154
|
+
target.startWrite()
|
|
155
|
+
} catch (e: IOException) {
|
|
156
|
+
Log.w(TAG, "cannot write report.json: $e")
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
out.write(o.toString().toByteArray())
|
|
161
|
+
target.finishWrite(out)
|
|
162
|
+
} catch (e: IOException) {
|
|
163
|
+
target.failWrite(out)
|
|
164
|
+
Log.w(TAG, "cannot write report.json: $e")
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private fun newInstallId(): String = ByteArray(16).also { SecureRandom().nextBytes(it) }.joinToString("") { "%02x".format(it) }
|
|
169
|
+
}
|