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
|
@@ -86,6 +86,8 @@ export interface AkanNativeConfig {
|
|
|
86
86
|
/** Painted before the page loads, to avoid a white flash (SH-3). */
|
|
87
87
|
backgroundColor?: string;
|
|
88
88
|
backgroundColorDark?: string;
|
|
89
|
+
/** The web inspector in a release build too (a debug build always has it): internal test builds only. */
|
|
90
|
+
devtools?: boolean;
|
|
89
91
|
};
|
|
90
92
|
/** Overrides for permission usage texts, e.g. { NSCameraUsageDescription: "..." }. */
|
|
91
93
|
usageDescriptions?: Record<string, string>;
|
|
@@ -112,10 +114,20 @@ export interface AkanNativeConfig {
|
|
|
112
114
|
permissions?: { camera?: string | true; microphone?: string | true; location?: string | true };
|
|
113
115
|
/**
|
|
114
116
|
* Over-the-air updates (UP-2 web bundles on iOS / Android, UP-1 app updates on macOS), used by
|
|
115
|
-
* @akanjs/native/plugins/updates. `akan-native update keygen` makes the signing key and prints
|
|
116
|
-
* `akan-native update publish <platform>` writes signed releases for `url`.
|
|
117
|
+
* @akanjs/native/plugins/updates. `akan-native update keygen` makes the signing key and prints its public key;
|
|
118
|
+
* `akan-native update publish <platform>` writes signed releases for `url`. `publicKeys` lists the keys a phone
|
|
119
|
+
* trusts, the online signing key first and offline spares after (a key revokes only keys before it); `publicKey` is
|
|
120
|
+
* the one-key form, and a desktop app trusts the first. `report.url` is where a phone posts what became of each
|
|
121
|
+
* release, at its next launch or return to the foreground.
|
|
117
122
|
*/
|
|
118
|
-
updates?: {
|
|
123
|
+
updates?: {
|
|
124
|
+
url: string;
|
|
125
|
+
publicKey?: string;
|
|
126
|
+
publicKeys?: string[];
|
|
127
|
+
channel?: string;
|
|
128
|
+
readyTimeout?: number;
|
|
129
|
+
report?: { url: string };
|
|
130
|
+
};
|
|
119
131
|
/** Links that open the app, delivered through the app plugin's urlOpen (and getLaunchUrl). */
|
|
120
132
|
deepLinks?: {
|
|
121
133
|
/** Custom URL schemes (myapp://...). */
|
|
@@ -250,10 +262,10 @@ export interface AkanNativeConfig {
|
|
|
250
262
|
recovery?: "errorPage" | "reload";
|
|
251
263
|
/**
|
|
252
264
|
* The main window from its first frame: `fullscreen` (borderless, on the display the window opens on)
|
|
253
|
-
*
|
|
254
|
-
* otherwise (DesktopContext.launch.setWindow). Default false for
|
|
265
|
+
* `skipTaskbar` (no taskbar button; Windows and Linux) and `frameless` (no title bar or border). A plugin's
|
|
266
|
+
* launch phase may still decide otherwise (DesktopContext.launch.setWindow). Default false for all three.
|
|
255
267
|
*/
|
|
256
|
-
window?: { fullscreen?: boolean; skipTaskbar?: boolean };
|
|
268
|
+
window?: { fullscreen?: boolean; skipTaskbar?: boolean; frameless?: boolean };
|
|
257
269
|
/**
|
|
258
270
|
* Windows: "auto" answers the page's getDisplayMedia() with the first screen at once, without the picker or a
|
|
259
271
|
* user gesture: a remote-support session on an unattended screen. It is Chromium's switch for automated media
|
|
@@ -262,13 +274,29 @@ export interface AkanNativeConfig {
|
|
|
262
274
|
* offered. macOS and Linux ignore it.
|
|
263
275
|
*/
|
|
264
276
|
screenCapture?: "picker" | "auto";
|
|
277
|
+
/**
|
|
278
|
+
* Windows installer: "bootstrapper" (default) carries Microsoft's 2 MB setup that downloads the WebView2
|
|
279
|
+
* Runtime when the PC has none; "standalone" carries the whole runtime (~200 MB), for a PC that is offline or
|
|
280
|
+
* behind a proxy at install. Either runs only where no runtime is installed.
|
|
281
|
+
*/
|
|
282
|
+
webview2?: "bootstrapper" | "standalone";
|
|
283
|
+
/** Windows: the installed app's Publisher (Apps & features) and its executable's company name (default: the app name). */
|
|
284
|
+
publisher?: string;
|
|
265
285
|
/**
|
|
266
286
|
* A server the app starts beside its window (akanjs `native.desktop.server`). `dir` is copied to
|
|
267
287
|
* the app's resources (`server/`), and `entry` there runs on the app's own Bun with `env`, bound to
|
|
268
288
|
* a loopback port picked at launch; the page reads its URL as PUBLIC_AKAN_SERVER_URL. The launcher
|
|
269
289
|
* sets PORT, JWT_SECRET, the data folders (`<app local data>/server`) and the listen host itself.
|
|
290
|
+
* `restart`: crashes in a row before it gives up (5), or "forever"; `watchdog`: its health route asked every
|
|
291
|
+
* `intervalMs`, `failures` missed in a row restarting it.
|
|
270
292
|
*/
|
|
271
|
-
server?: {
|
|
293
|
+
server?: {
|
|
294
|
+
dir: string;
|
|
295
|
+
entry: string;
|
|
296
|
+
env?: Record<string, string>;
|
|
297
|
+
restart?: number | "forever";
|
|
298
|
+
watchdog?: { intervalMs: number; failures: number };
|
|
299
|
+
};
|
|
272
300
|
/**
|
|
273
301
|
* A folder of executables the app carries (akanjs `bin`): copied to the app's resources (`bin/`) and put
|
|
274
302
|
* first on the app's PATH at launch, so its plugins and its server find them by name before the computer's.
|
|
@@ -35,6 +35,8 @@ export interface BundleInfo {
|
|
|
35
35
|
nativeApi: { hash: string; inputs: NativeApi };
|
|
36
36
|
/** sha256 of the web files (index.html as served and public/), for update integrity later. */
|
|
37
37
|
web: { hash: string; files: number };
|
|
38
|
+
/** A phone build with updates: what its binary reads (updates.json), the floor of every release's sequence included. */
|
|
39
|
+
updates?: { protocol: 2; url: string; channel: string; keyIds: string[]; embeddedSequence: number };
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
/** "0.1.4" → "0.1", "1.2.0" → "1". */
|
|
@@ -16,11 +16,11 @@ const KNOWN: Shape = {
|
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
18
|
env: { defaults: true, platforms: { web: true, macos: true, windows: true, linux: true, ios: true, android: true } },
|
|
19
|
-
shell: { backgroundColor: true, backgroundColorDark: true },
|
|
19
|
+
shell: { backgroundColor: true, backgroundColorDark: true, devtools: true },
|
|
20
20
|
usageDescriptions: true,
|
|
21
21
|
privacy: { tracking: true, trackingDomains: true, collectedDataTypes: true, accessedApis: true },
|
|
22
22
|
permissions: { camera: true, microphone: true, location: true },
|
|
23
|
-
updates: { url: true, publicKey: true, channel: true, readyTimeout: true },
|
|
23
|
+
updates: { url: true, publicKey: true, publicKeys: true, channel: true, readyTimeout: true, report: { url: true } },
|
|
24
24
|
deepLinks: { schemes: true, domains: { "[]": { host: true, pathPrefixes: true } } },
|
|
25
25
|
native: {
|
|
26
26
|
ios: { infoPlist: true, entitlements: true },
|
|
@@ -33,9 +33,11 @@ const KNOWN: Shape = {
|
|
|
33
33
|
desktop: {
|
|
34
34
|
quitOnLastWindowClosed: true,
|
|
35
35
|
recovery: true,
|
|
36
|
-
window: { fullscreen: true, skipTaskbar: true },
|
|
36
|
+
window: { fullscreen: true, skipTaskbar: true, frameless: true },
|
|
37
37
|
screenCapture: true,
|
|
38
|
-
|
|
38
|
+
webview2: true,
|
|
39
|
+
publisher: true,
|
|
40
|
+
server: { dir: true, entry: true, env: true, restart: true, watchdog: { intervalMs: true, failures: true } },
|
|
39
41
|
bin: true,
|
|
40
42
|
dmg: {
|
|
41
43
|
background: true,
|
|
@@ -10,6 +10,8 @@ export interface DesktopServerConfig {
|
|
|
10
10
|
dir: string;
|
|
11
11
|
entry: string;
|
|
12
12
|
env: Record<string, string>;
|
|
13
|
+
restart?: number | "forever";
|
|
14
|
+
watchdog?: { intervalMs: number; failures: number };
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -54,5 +56,13 @@ export function validateDesktopServer(
|
|
|
54
56
|
else if (typeof value !== "string") problems.push(`desktop.server.env.${key} must be a string`);
|
|
55
57
|
else env[key] = value;
|
|
56
58
|
}
|
|
57
|
-
|
|
59
|
+
const { restart, watchdog } = server;
|
|
60
|
+
if (restart !== undefined && restart !== "forever" && !(Number.isInteger(restart) && restart > 0))
|
|
61
|
+
problems.push('desktop.server.restart must be "forever" or a count of crashes in a row from 1');
|
|
62
|
+
const positive = (value: unknown) => Number.isInteger(value) && (value as number) > 0;
|
|
63
|
+
if (watchdog !== undefined && !(watchdog && positive(watchdog.intervalMs) && positive(watchdog.failures)))
|
|
64
|
+
problems.push("desktop.server.watchdog must be { intervalMs, failures }, both whole numbers from 1");
|
|
65
|
+
return problems.length === before && dir
|
|
66
|
+
? { dir, entry, env, ...(restart !== undefined ? { restart } : {}), ...(watchdog ? { watchdog } : {}) }
|
|
67
|
+
: null;
|
|
58
68
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { CliError } from "./log.ts";
|
|
3
|
+
import { type MacosNotarization, type MacosSigning, macosDistributionFromEnv } from "./macossigning.ts";
|
|
4
|
+
import { type WindowsSigning, windowsSigningFromEnv } from "./windowssigning.ts";
|
|
5
|
+
|
|
6
|
+
export interface DistributionRequest {
|
|
7
|
+
/** Sign the app with the identity the env names (Developer ID on macOS, Authenticode on Windows). */
|
|
8
|
+
sign?: boolean;
|
|
9
|
+
/** macOS: upload the signed app to Apple's notary service. */
|
|
10
|
+
notarize?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Distribution {
|
|
14
|
+
macos?: { signing?: MacosSigning; notarize?: MacosNotarization };
|
|
15
|
+
windows?: { signing?: WindowsSigning };
|
|
16
|
+
/** One line saying who signs and where the app goes, for before the build; null when nothing is signed. */
|
|
17
|
+
notice: string | null;
|
|
18
|
+
/** The env names that configure a signature this build was not asked to make. */
|
|
19
|
+
unused: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const SIGNING_ENV =
|
|
23
|
+
/^AKAN_NATIVE_(?:MACOS_(?:IDENTITY|CERTIFICATE|NOTARY_PROFILE|NOTARY_KEY)|WINDOWS_(?:CERTIFICATE|THUMBPRINT|SIGN_COMMAND))$/;
|
|
24
|
+
|
|
25
|
+
export function requestedDistribution(
|
|
26
|
+
platform: string,
|
|
27
|
+
profile: "debug" | "release",
|
|
28
|
+
{ sign = false, notarize = false }: DistributionRequest,
|
|
29
|
+
env: Record<string, string | undefined> = process.env,
|
|
30
|
+
): Distribution {
|
|
31
|
+
const unused = Object.keys(env)
|
|
32
|
+
.filter((name) => SIGNING_ENV.test(name) && env[name]?.trim())
|
|
33
|
+
.sort();
|
|
34
|
+
if (!sign && !notarize) return { notice: null, unused };
|
|
35
|
+
if (platform !== "macos" && platform !== "windows")
|
|
36
|
+
throw new CliError(`--sign and --notarize are for a macOS or Windows app, not ${platform}`);
|
|
37
|
+
if (profile !== "release") throw new CliError("--sign and --notarize take a release build");
|
|
38
|
+
if (notarize && platform !== "macos") throw new CliError("--notarize is Apple's: a Windows app is only signed");
|
|
39
|
+
if (notarize && !sign) throw new CliError("--notarize takes --sign: Apple notarizes a signed app only");
|
|
40
|
+
if (platform === "windows") {
|
|
41
|
+
const signing = windowsSigningFromEnv(env);
|
|
42
|
+
if (!signing)
|
|
43
|
+
throw new CliError(
|
|
44
|
+
"--sign needs the identity: AKAN_NATIVE_WINDOWS_CERTIFICATE (+ _PASSWORD), AKAN_NATIVE_WINDOWS_THUMBPRINT or AKAN_NATIVE_WINDOWS_SIGN_COMMAND",
|
|
45
|
+
);
|
|
46
|
+
const by = signing.command
|
|
47
|
+
? `the signing command ${basename(signing.command[0] ?? "")}, which may send each file to its signing service`
|
|
48
|
+
: signing.thumbprint
|
|
49
|
+
? `the certificate ${signing.thumbprint}`
|
|
50
|
+
: `the certificate in ${basename(signing.certificate?.path ?? "")}`;
|
|
51
|
+
return { windows: { signing }, notice: `Signs the Windows app with ${by}.`, unused: [] };
|
|
52
|
+
}
|
|
53
|
+
const { signing, notarize: notarization } = macosDistributionFromEnv(env);
|
|
54
|
+
if (!signing)
|
|
55
|
+
throw new CliError(
|
|
56
|
+
"--sign needs the identity: AKAN_NATIVE_MACOS_IDENTITY, or AKAN_NATIVE_MACOS_CERTIFICATE (+ _PASSWORD)",
|
|
57
|
+
);
|
|
58
|
+
if (notarize && !notarization)
|
|
59
|
+
throw new CliError(
|
|
60
|
+
"--notarize needs the credentials: AKAN_NATIVE_MACOS_NOTARY_PROFILE, or AKAN_NATIVE_MACOS_NOTARY_KEY + _KEY_ID",
|
|
61
|
+
);
|
|
62
|
+
const identity = signing.identity ?? `the certificate in ${basename(signing.certificate?.path ?? "")}`;
|
|
63
|
+
const notary = notarization?.profile
|
|
64
|
+
? `notarytool profile ${notarization.profile}`
|
|
65
|
+
: `API key ${notarization?.keyId}`;
|
|
66
|
+
return {
|
|
67
|
+
macos: { signing, ...(notarize && notarization ? { notarize: notarization } : {}) },
|
|
68
|
+
notice: notarize
|
|
69
|
+
? `Signs the macOS app as ${identity} and uploads it to Apple's notary service (${notary}).`
|
|
70
|
+
: `Signs the macOS app as ${identity}; nothing is uploaded without --notarize.`,
|
|
71
|
+
unused: [],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -25,15 +25,17 @@ export interface ResolvedConfig
|
|
|
25
25
|
web: Required<Pick<AkanNativeConfig["web"], "dir" | "base">> & { build?: string; devEntry?: string };
|
|
26
26
|
plugins: string[];
|
|
27
27
|
env: EnvConfig;
|
|
28
|
-
shell: { backgroundColor: string; backgroundColorDark: string };
|
|
28
|
+
shell: { backgroundColor: string; backgroundColorDark: string; devtools: boolean };
|
|
29
29
|
usageDescriptions: Record<string, string>;
|
|
30
30
|
permissions: PermissionsConfig;
|
|
31
31
|
deepLinks: { schemes: string[]; domains: { host: string; pathPrefixes: string[] }[] };
|
|
32
32
|
desktop: {
|
|
33
33
|
quitOnLastWindowClosed: boolean;
|
|
34
34
|
recovery: "errorPage" | "reload";
|
|
35
|
-
window: { fullscreen: boolean; skipTaskbar: boolean };
|
|
35
|
+
window: { fullscreen: boolean; skipTaskbar: boolean; frameless: boolean };
|
|
36
36
|
screenCapture: "picker" | "auto";
|
|
37
|
+
webview2: "bootstrapper" | "standalone";
|
|
38
|
+
publisher?: string;
|
|
37
39
|
server?: DesktopServerConfig;
|
|
38
40
|
/** Absolute. */
|
|
39
41
|
bin?: string;
|
|
@@ -201,13 +203,19 @@ export function projectFromConfig(raw: AkanNativeConfig, appDir: string, options
|
|
|
201
203
|
const recovery = raw.desktop?.recovery;
|
|
202
204
|
if (recovery !== undefined && recovery !== "errorPage" && recovery !== "reload")
|
|
203
205
|
problems.push(`desktop.recovery must be "errorPage" or "reload" (got ${JSON.stringify(recovery)})`);
|
|
204
|
-
for (const key of ["fullscreen", "skipTaskbar"] as const) {
|
|
206
|
+
for (const key of ["fullscreen", "skipTaskbar", "frameless"] as const) {
|
|
205
207
|
const value = raw.desktop?.window?.[key];
|
|
206
208
|
if (value !== undefined && typeof value !== "boolean") problems.push(`desktop.window.${key} must be a boolean`);
|
|
207
209
|
}
|
|
208
210
|
const screenCapture = raw.desktop?.screenCapture;
|
|
209
211
|
if (screenCapture !== undefined && screenCapture !== "picker" && screenCapture !== "auto")
|
|
210
212
|
problems.push(`desktop.screenCapture must be "picker" or "auto" (got ${JSON.stringify(screenCapture)})`);
|
|
213
|
+
const webview2 = raw.desktop?.webview2;
|
|
214
|
+
if (webview2 !== undefined && webview2 !== "bootstrapper" && webview2 !== "standalone")
|
|
215
|
+
problems.push(`desktop.webview2 must be "bootstrapper" or "standalone" (got ${JSON.stringify(webview2)})`);
|
|
216
|
+
const publisher = raw.desktop?.publisher;
|
|
217
|
+
if (publisher !== undefined && (typeof publisher !== "string" || !publisher.trim() || /["\r\n]/.test(publisher)))
|
|
218
|
+
problems.push("desktop.publisher must be a name on one line, without a double quote");
|
|
211
219
|
if (raw.android?.autoplay !== undefined && typeof raw.android.autoplay !== "boolean")
|
|
212
220
|
problems.push("android.autoplay must be a boolean");
|
|
213
221
|
const bin = raw.desktop?.bin;
|
|
@@ -253,6 +261,7 @@ export function projectFromConfig(raw: AkanNativeConfig, appDir: string, options
|
|
|
253
261
|
const shell = {
|
|
254
262
|
backgroundColor: raw.shell?.backgroundColor ?? "#ffffff",
|
|
255
263
|
backgroundColorDark: raw.shell?.backgroundColorDark ?? raw.shell?.backgroundColor ?? "#000000",
|
|
264
|
+
devtools: raw.shell?.devtools === true,
|
|
256
265
|
};
|
|
257
266
|
const icon = typeof raw.icon === "string" ? { image: raw.icon } : raw.icon;
|
|
258
267
|
const splashColor = raw.splash?.backgroundColor;
|
|
@@ -279,9 +288,12 @@ export function projectFromConfig(raw: AkanNativeConfig, appDir: string, options
|
|
|
279
288
|
quitOnLastWindowClosed: raw.desktop?.quitOnLastWindowClosed ?? true,
|
|
280
289
|
recovery: raw.desktop?.recovery === "reload" ? "reload" : "errorPage",
|
|
281
290
|
screenCapture: raw.desktop?.screenCapture === "auto" ? "auto" : "picker",
|
|
291
|
+
webview2: raw.desktop?.webview2 === "standalone" ? "standalone" : "bootstrapper",
|
|
292
|
+
...(typeof raw.desktop?.publisher === "string" ? { publisher: raw.desktop.publisher.trim() } : {}),
|
|
282
293
|
window: {
|
|
283
294
|
fullscreen: raw.desktop?.window?.fullscreen === true,
|
|
284
295
|
skipTaskbar: raw.desktop?.window?.skipTaskbar === true,
|
|
296
|
+
frameless: raw.desktop?.window?.frameless === true,
|
|
285
297
|
},
|
|
286
298
|
...(desktopServer ? { server: desktopServer } : {}),
|
|
287
299
|
...(binDir ? { bin: binDir } : {}),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { assertSameServer, publishAppUpdate } from "../platforms/desktop-update.ts";
|
|
6
6
|
import { BUNDLE_FILE, readBundleInfo } from "./compat.ts";
|
|
@@ -8,9 +8,12 @@ import { CliError, log } from "./log.ts";
|
|
|
8
8
|
import type { BuildContext } from "./prepare.ts";
|
|
9
9
|
import {
|
|
10
10
|
assertChannel,
|
|
11
|
+
type DesktopArch,
|
|
11
12
|
hostArch,
|
|
13
|
+
sealManifest,
|
|
12
14
|
signingKey,
|
|
13
15
|
signManifest,
|
|
16
|
+
type UpdateEnvelope,
|
|
14
17
|
type UpdateManifest,
|
|
15
18
|
updateKeyPath,
|
|
16
19
|
webManifest,
|
|
@@ -23,17 +26,34 @@ export interface PublishedRelease {
|
|
|
23
26
|
/** `<out>/<os>-<arch>` for a desktop app, `<out>/<platform>` for a phone's web bundle. */
|
|
24
27
|
dir: string;
|
|
25
28
|
manifest: UpdateManifest;
|
|
29
|
+
/** The file installed apps read: `<dir>/<channel>.json`, or a phone's `<dir>/<channel>/<nativeApi>.json`. */
|
|
30
|
+
file: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The largest sequence published to `channel` in `dir`: a desktop's `<channel>.json`, every `<channel>/*.json` of a phone. */
|
|
34
|
+
function publishedSequence(dir: string, channel: string): number | null {
|
|
35
|
+
const sequences: unknown[] = [];
|
|
36
|
+
const v1 = join(dir, `${channel}.json`);
|
|
37
|
+
if (existsSync(v1)) sequences.push((JSON.parse(readFileSync(v1, "utf8")) as Partial<UpdateManifest>).sequence);
|
|
38
|
+
const v2 = join(dir, channel);
|
|
39
|
+
if (existsSync(v2))
|
|
40
|
+
for (const name of readdirSync(v2).filter((name) => name.endsWith(".json"))) {
|
|
41
|
+
const envelope = JSON.parse(readFileSync(join(v2, name), "utf8")) as Partial<UpdateEnvelope>;
|
|
42
|
+
const manifest = JSON.parse(Buffer.from(envelope.manifest ?? "", "base64").toString("utf8") || "{}");
|
|
43
|
+
sequences.push((manifest as Partial<UpdateManifest>).sequence);
|
|
44
|
+
}
|
|
45
|
+
const numbers = sequences.filter((sequence): sequence is number => typeof sequence === "number");
|
|
46
|
+
return numbers.length ? Math.max(...numbers) : null;
|
|
26
47
|
}
|
|
27
48
|
|
|
28
49
|
/**
|
|
29
|
-
* The publish time in seconds, and past
|
|
30
|
-
*
|
|
50
|
+
* The publish time in seconds, and past every release of the channel in `dir`: apps take only a larger sequence, so a
|
|
51
|
+
* clock behind the last publisher's would make this release look older to every one of them. A phone's native APIs
|
|
52
|
+
* share one count, so a binary moving to the next generation never meets a smaller number.
|
|
31
53
|
*/
|
|
32
54
|
export function nextSequence(dir: string, channel: string, now = Math.floor(Date.now() / 1000)): number {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
const previous = (JSON.parse(readFileSync(path, "utf8")) as Partial<UpdateManifest>).sequence;
|
|
36
|
-
if (typeof previous !== "number" || previous < now) return now;
|
|
55
|
+
const previous = publishedSequence(dir, channel);
|
|
56
|
+
if (previous === null || previous < now) return now;
|
|
37
57
|
if (previous > now)
|
|
38
58
|
log.warn(
|
|
39
59
|
`the ${channel} release already here has sequence ${previous}, later than this computer's clock (${now}): publishing as ${previous + 1}. Check the clock of the computer that publishes.`,
|
|
@@ -41,10 +61,10 @@ export function nextSequence(dir: string, channel: string, now = Math.floor(Date
|
|
|
41
61
|
return previous + 1;
|
|
42
62
|
}
|
|
43
63
|
|
|
44
|
-
/** `<out>/<os>-<arch>` for a desktop release, `<out>/<platform>` for a phone's web bundle. */
|
|
45
|
-
export function releaseDir(out: string, platform: ReleasePlatform): string {
|
|
64
|
+
/** `<out>/<os>-<arch>` for a desktop release (the arch it was built for), `<out>/<platform>` for a phone's web bundle. */
|
|
65
|
+
export function releaseDir(out: string, platform: ReleasePlatform, arch?: DesktopArch): string {
|
|
46
66
|
const desktop = platform === "macos" || platform === "windows" || platform === "linux";
|
|
47
|
-
return join(out, desktop ? `${platform}-${hostArch()}` : platform);
|
|
67
|
+
return join(out, desktop ? `${platform}-${arch ?? hostArch()}` : platform);
|
|
48
68
|
}
|
|
49
69
|
|
|
50
70
|
/** Refuses before the build a desktop release the apps of its channel would refuse for its server (assertSameServer). */
|
|
@@ -53,16 +73,25 @@ export function assertServerOfChannel(
|
|
|
53
73
|
platform: ReleasePlatform,
|
|
54
74
|
out: string,
|
|
55
75
|
channel: string,
|
|
76
|
+
arch?: DesktopArch,
|
|
56
77
|
): void {
|
|
57
78
|
if (platform === "macos" || platform === "windows" || platform === "linux")
|
|
58
|
-
assertSameServer(releaseDir(out, platform), channel, !!config.desktop?.server);
|
|
79
|
+
assertSameServer(releaseDir(out, platform, arch), channel, !!config.desktop?.server);
|
|
59
80
|
}
|
|
60
81
|
|
|
61
|
-
/**
|
|
62
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Refuses before the build a release that could not be signed: no key on this machine, another app's, or for a desktop
|
|
84
|
+
* app one the app does not trust (it trusts the first of updates.publicKeys).
|
|
85
|
+
*/
|
|
86
|
+
export function assertSigningKey(
|
|
87
|
+
config: { app: { id: string }; updates?: { publicKeys: string[] } | null },
|
|
88
|
+
platform?: ReleasePlatform,
|
|
89
|
+
): void {
|
|
63
90
|
if (!config.updates)
|
|
64
|
-
throw new CliError("akan-native.config.ts has no updates: { url,
|
|
65
|
-
signingKey(updateKeyPath(config.app.id), config.updates.
|
|
91
|
+
throw new CliError("akan-native.config.ts has no updates: { url, publicKeys } (run `akan-native update keygen`)");
|
|
92
|
+
const { index } = signingKey(updateKeyPath(config.app.id), config.updates.publicKeys);
|
|
93
|
+
if (index !== 0 && (platform === "macos" || platform === "windows" || platform === "linux"))
|
|
94
|
+
throw new CliError("a desktop app trusts the first of updates.publicKeys only: sign with that key");
|
|
66
95
|
}
|
|
67
96
|
|
|
68
97
|
/**
|
|
@@ -77,7 +106,16 @@ function writeSigned(dir: string, channel: string, bytes: Uint8Array, signature:
|
|
|
77
106
|
renameSync(`${json}.tmp`, json);
|
|
78
107
|
}
|
|
79
108
|
|
|
80
|
-
/**
|
|
109
|
+
/** A phone's envelope replaces the last one in a single rename, so no reader meets half of either. */
|
|
110
|
+
function writeEnvelope(dir: string, channel: string, nativeApi: string, envelope: UpdateEnvelope): string {
|
|
111
|
+
const file = join(dir, channel, `${nativeApi}.json`);
|
|
112
|
+
mkdirSync(join(dir, channel), { recursive: true });
|
|
113
|
+
writeFileSync(`${file}.tmp`, `${JSON.stringify(envelope)}\n`);
|
|
114
|
+
renameSync(`${file}.tmp`, file);
|
|
115
|
+
return file;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Writes the release of `artifact` (the app a release build made) and its signed manifest under `out`. */
|
|
81
119
|
export async function publishRelease(
|
|
82
120
|
ctx: BuildContext,
|
|
83
121
|
platform: ReleasePlatform,
|
|
@@ -87,12 +125,12 @@ export async function publishRelease(
|
|
|
87
125
|
): Promise<PublishedRelease> {
|
|
88
126
|
const { config } = ctx.project;
|
|
89
127
|
if (!config.updates)
|
|
90
|
-
throw new CliError("akan-native.config.ts has no updates: { url,
|
|
128
|
+
throw new CliError("akan-native.config.ts has no updates: { url, publicKeys } (run `akan-native update keygen`)");
|
|
91
129
|
const desktop = platform === "macos" || platform === "windows" || platform === "linux" ? platform : null;
|
|
92
130
|
|
|
93
|
-
const dir = releaseDir(out, platform);
|
|
131
|
+
const dir = releaseDir(out, platform, ctx.arch);
|
|
94
132
|
assertChannel(channel);
|
|
95
|
-
assertSigningKey(config);
|
|
133
|
+
assertSigningKey(config, platform);
|
|
96
134
|
const keyPath = updateKeyPath(config.app.id);
|
|
97
135
|
const sequence = nextSequence(dir, channel);
|
|
98
136
|
|
|
@@ -113,9 +151,18 @@ export async function publishRelease(
|
|
|
113
151
|
});
|
|
114
152
|
}
|
|
115
153
|
const bytes = new TextEncoder().encode(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
116
|
-
const signature = `${signManifest(bytes, keyPath, config.updates.publicKey)}\n`;
|
|
117
154
|
mkdirSync(dir, { recursive: true });
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
155
|
+
let file: string;
|
|
156
|
+
if (desktop) {
|
|
157
|
+
writeSigned(dir, channel, bytes, `${signManifest(bytes, keyPath, config.updates.publicKeys)}\n`);
|
|
158
|
+
file = join(dir, `${channel}.json`);
|
|
159
|
+
} else
|
|
160
|
+
file = writeEnvelope(
|
|
161
|
+
dir,
|
|
162
|
+
channel,
|
|
163
|
+
manifest.nativeApi ?? "",
|
|
164
|
+
sealManifest(bytes, keyPath, config.updates.publicKeys),
|
|
165
|
+
);
|
|
166
|
+
for (const leftover of leftovers) rmSync(leftover, { force: true });
|
|
167
|
+
return { dir, manifest, file };
|
|
121
168
|
}
|