diff --git a/DEVELOPMENT.adoc b/DEVELOPMENT.adoc index eb09097..8ed2741 100644 --- a/DEVELOPMENT.adoc +++ b/DEVELOPMENT.adoc @@ -9,151 +9,26 @@ == Architecture -The Chrome and Electron versions of the app share the exact same UI code, but in both systems the UI code must communicate with a separate background system that can accomplish the tasks needed against the running Fulcro app. The FUlcro app could be in a browser, a CLJ runtime, or native app (e.g. mobile). The electron app communicates with the Fulcro app via websockets, whereas the Inspect tool in Chrome uses injected content scripts, a background service worker to communicate with a dev tools panel. +The Chrome and Electron versions of the app share the exact same UI code, but in both systems the UI code must +communicate with a separate background system. +We use fulcro-devtools-remote to abstract this away. [d2] ----- -Fulcro App <-> Adapter: serialized messages -Adapter <-> Communication Worker: serialized -Communication Worker <-> Dev Tools UI: serialized +Fulcro App <-> Fulcro Devtool Remote +Fulcro Devtool Remote <-> Dev Tools UI: serialized ----- -The Adapter is the inspect preload, which lives in the Fulcro primary repository. +See Fulcro Devtool Remote for more details. -=== Dev Tool Network (remote) +== Building -The dev tool is a Fulcro app. The actual communication from the dev tool happens via mutations and loads, just like any -other Fulcro application. The magic all happens in the custom remote that is created in the tool itself. The remote -is rather simple, it is a local Pathom processor that puts a `send-message` function in the `env`. But, the is slightly complicated -by the fact that the communication itself could be going over a websocket or chrome devtool ports. - -Thus, the app/remote has to be created based on IF it is a Chrome plugin, or an electron app. Thus, there is slightly -different code for starting the devtool, in two different namespaces and source folders. - -The startup for the chrome tool is in `f.i.c.devtool.main`. The one for Electron is `f.i.electron.renderer.main`. - -Because the remotes need to directly access certain details of the embedded environment, the code to handle a remote message -is duplicated. Thus, if you need to support new messages in the tool, you must change both namespaces (see `handle-remote-message`). - -=== Chrome vs Websocket Preload - -The preloads in Fulcro Inspect basically do the same thing: They start a core.async loop that waits for incoming messages from the Dev Tools UI (through the intermediaries). - -The Chrome preload installs a message event listener on the `js/window` (of the app your working on). - -In order to ensure that the inspect part of your app doesn't send messages before the devtool is ready, it waits to start the send message processing until it receives a `fulcro-inspect-start-consume` message. - -The devtool sends messages identified by `fulcro-inspect-devtool-message`. These are handled by transit decoding the content and the processing the content. -See `handle-devtool-message` in the `inspect-client` ns. - -Some messages from devtools are requests to return information (e.g. history steps from the database), and some are requests to run transactions on behalf of the tooling (e.g. a load) - -The websocket preload hooks the Fulcro App up to a websocket, and tries to connect it to a running Electron-based dev tool. It uses the exact same transit decoding, and forwarding to `handle-devtools-message`. - -The websocket support just avoids sending messages until the websocket itself reports that it is open. - -The communications from the Fulcro App are sent into a core async channel (`inspect/send-ch`) that has a dropping buffer of 50k entries. This makes it possible for the app to start and do various things before you ever connect the devtool, but prevents missed messages. - -Both preload communication systems read this channel and then forward the messages on to their respective tooling client. - -=== Chrome Communication - -On startup, the Fulcro Inspect Chrome Extension creates a background service worker, has content scripts that it injects into every web page, and has the devtool pane itself. - -==== Background Worker - -The background service worker registers for `connect` events with the following two names: - -* `fulcro-inspect-devtool` - Communication to/from the devtool UI -* `fulcro-inspect-remote` - Communication to/from the Fulcro App - -and ignores any other connect events from the page. - -==== Content Script - -The Content Script (which is injected onto the web page) looks for an indication the Fulcro app(s) exist on the page. When it detects this, it opens a connection (to the bg worker) and adds a listener that forwards incoming `fulcro-inspect-devtool-message` as a window event, and otherwise tries to determine if the incoming message is a *response* to an outgoing request. - -Call/response tracking is done by giving the outgoing request a `__fulcro-insect-message-id` (misspelling is in the code). Such messages are tracked in an atom of `active-messages*` which holds a temporary core.async channel on which the response is expected. The response will include this message id, and can then be put on that temporary channel. - -The content script also adds a listener for `fulcro-inspect-remote-message` events, which it places on a core async channel. That channel is used to foward those messages on to the open port (back to the background worker). - -.Communication FROM background worker -[d2] ------ -BG -> Content Script : port (type: devtool-message) -Content Script -> Window Event -Window Event -> Inspect Preload: (type: handle-devtool-message) -Inspect Preload -> Fulcro App(s) ------ - -.Communication To background worker -[d2] ------ -Fullcro App(s) -> post-message -post-message -> Async Send Channel -Async Send Channel -> Inspect Preload: (async read loop) -Inspect Preload -> Window Event: (type: fulcro-inspect-remote-message) -Window Event -> Content Script -Content Script -> BG: port ------ - -The background worker on chrome has a separate port that the devtool opens, and it communicates over that port. - -.Background worker to Dev Tool -[d2] ------ -BG <-> Devtool : port ------ - -Communication at the DevTool API level is done with a Fulcro Remote that posts messages to that port. - -==== Background Script - -The background script talks to BOTH the content script on the web page, and the dev tools pane. It keeps track of the open ports (by name), and essentially is just a proxying service. - -== Electron - -=== Communication - -The Electron app has a slightly different set of mechanisms for communication. The Dev Tool code is shared from -the Chrome-based tool, since Chrome is actually used as the rendering engine in Electron. The difference is in how -messages are passed to/from the tool the Fulcro App. - -Electron has to use the network to talk to the application, which is running in an entirely different process. We -use websockets for easy bi-directional communication. The security layers in Electron create the same kind of -complexity as in the Chrome plugin, so the architecture is very similar. - -Communication from the Fulcro App still goes through a preload, but in this case the preload does not need -an injected content script. Instead, it simply opens a websocket connection to a (hopefully-running) Fulcro Inspect -server (running within Electron). - -.Communication FROM Electron App to Fulcro App -[d2] ------ -Devtool -> ipcRenderer -ipcRenderer -> ipcMain: event crosses security boundary (renderer -> server) -ipcMain -> Inspect WS -Inspect WS -> Fulcro App(s): handle-devtool-message ------ - -.Communication from Fulcro to Electron -[d2] ------ -Fullcro App(s) -> post-message -post-message -> Async Send Channel -Async Send Channel -> Inspect WS: (async read loop) -Inspect WS -> Electron Server -Electron Server -> ipcRenderer: (.send webContents event) -ipcRenderer -> handle-remote-message ------ - -=== Building - -Building MacOS releases is done as follows: +Building MacOS Electron releases is done as follows: ---- $ npm install $ cd shells/electron +$ vi package.json # Update version number $ yarn $ cd ../.. $ shadow-cljs release electron-main electron-renderer @@ -161,21 +36,9 @@ $ cd shells/electron $ electron-builder build -m ---- -Building the Windows and Linux releases requires that you have Docker -installed, then you can run a Linux image with a wine-centric builder -via: - ----- -$ ./build-linux.sh -root@234987:/project# yarn -root@234987:/project# eletron-builder build -wl -root@234987:/project# exit -$ ls dist ----- - -and the resulting files will be in `dist`. +See the `Makefile` for other builds -= Chrome +== Chrome First, make sure to update the version number in `shells/chrome/manifest.edn`. diff --git a/shells/electron/build-linux.sh b/shells/electron/build-linux.sh deleted file mode 100755 index c48ba47..0000000 --- a/shells/electron/build-linux.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -docker run --platform linux/amd64 --rm -ti --env-file <(env | grep -iE 'DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS_TAG|TRAVIS|TRAVIS_REPO_|TRAVIS_BUILD_|TRAVIS_BRANCH|TRAVIS_PULL_REQUEST_|APPVEYOR_|CSC_|GH_|GITHUB_|BT_|AWS_|STRIP|BUILD_') --env ELECTRON_CACHE="/root/.cache/electron" --env ELECTRON_BUILDER_CACHE="/root/.cache/electron-builder" -v ${PWD}:/project -v ${PWD##*/}-node-modules:/project/node_modules -v ~/.cache/electron:/root/.cache/electron -v ~/.cache/electron-builder:/root/.cache/electron-builder electronuserland/builder:wine