@finamx/dev-host
v0.6.0
Published
Local developer host for `finamx-ext dev`: a prebuilt static React board that embeds your extensions through the real `@finamx/host-bridge`, the built-in base extensions, and the asset locator the CLI uses to find them. It is loaded by the CLI as an optio
Readme
@finamx/dev-host
Local developer host for finamx-ext dev: a prebuilt static React board that embeds your extensions through the real @finamx/host-bridge, the built-in base extensions, and the asset locator the CLI uses to find them. It is loaded by the CLI as an optional dependency and is not used directly.
npm i -D @finamx/dev-hostVersion 0.1.0, not yet published.
Embedding the board (0.4.0)
import '@finamx/dev-host/styles.css'; // scoped to .dh-root, never restyles the page
import { DevHostShell, CLOUD_CAPS, initStoreWithClient } from '@finamx/dev-host/ui';
initStoreWithClient(myControlClient); // any ControlClient, no CLI secret needed
<DevHostShell caps={CLOUD_CAPS} panels={[{ id: 'edges', label: 'Связи', element: <Edges /> }]} />;Capability flags (BoardCaps): autoPlace (project extensions place themselves), s2s, catalogAdd, baseAdd, edges,
mockEditor, cspFeed and themeTarget (document writes the theme variables on <html>, container on the board root only).
LOCAL_CAPS keep the finamx-ext dev behaviour; CLOUD_CAPS is for the cabinet. Cloud route types live in
@finamx/dev-host/cloud-protocol.
Security model
- Every server binds to loopback (127.0.0.1) only.
- A per-run secret travels in the URL fragment of the link printed by the CLI and is required as
Authorization: Beareron every control call. - Host and Origin headers are checked against DNS rebinding and CSRF; the board page runs under a strict CSP.
- The CLI never hands out the developer key: not to the UI, the extension origins, NDJSON or logs, and not through the environment of the processes it starts. The project
watchscript is your own code and runs with your user's permissions (it can read the credentials file like any program you run); the CLI says so in one line when it starts it, and--no-watchskips it. - Only mocks derived from the host domain are served; real data never enters the dev host.
NDJSON events (finamx-ext dev --json)
Every line on stdout is one JSON object with v: 1, ts and type. Strings that come from widgets, hosts or HTTP bodies are sanitised (control characters, bidi marks, length caps). The developer key and the board secret never appear. Nothing else is written to stdout.
| type | when | fields | bus log row |
|------|------|--------|-------------|
| dev.ready | the board and all origins are listening | url, hostId, status | — |
| dev.stopped | the CLI shuts the dev host down | status | — |
| bus.publish | a widget publishes a topic through the broker | extensionId, topic, to, schema, fields | шина |
| bus.subscribe | a widget subscribes to a topic | extensionId, topic | шина |
| invoke | a widget calls a domain method | extensionId, method, result, code, mock, mockPath, scope | invoke |
| schema.error | a published payload fails the topic schema | extensionId, topic, schema, fields | ошибки |
| scope.denied | a call is denied because the scope is not granted | extensionId, method, topic, scope, code | ошибки |
| csp.violation | a frame reports a CSP violation | extensionId, directive, blockedUrl | ошибки |
| build | the project watch build starts, succeeds or fails | extensionId, status, message | — |
| domain.changed | the poll sees a new domainHash for the selected host | hostId, previousHash, domainHash | — |
| domain.poll_failed | the domain poll failed; the cache is kept | hostId, message | — |
| domain.refreshed | "Обновить домен" pulled the domain | hostId, previousHash, domainHash, diff | — |
| consent.changed | a consent toggle was switched | extensionId, scope, granted | — |
| bridge.handshake | a frame completed the bridge handshake | extensionId | — |
| tokens.delivered | UI tokens were delivered to a frame | extensionId, theme | — |
| s2s.token | a local platform.s2s.user.v1 token was issued | extensionId, dir, aud, exp, scope | s2s |
| s2s.pull | the local pull endpoint answered | method, result, code, mock | s2s |
| s2s.push | a signed push was sent to the manifest webhook | extensionId, url, status, payload | s2s |
| catalog.added | a catalog artifact was verified and added | extensionId, status | — |
| error | any other dev host error | code, message | ошибки |
Built-in base extensions
- Instrument picker: publishes
fdc3.instrument. - JSON inspector/table: shows the payloads of any chosen topic.
- Topic mapper: maps a source topic to a target topic with JS code. The code runs through the host
repl.jsmethod backed by@finamx/repl, because the platform CSP forbids eval inside a widget. The function may be async; a result that isundefinedor cannot be sent as JSON is shown as an error and nothing is published.repl.jsis the dev host's own method and is offered to this built-in widget only: a project or catalog extension that listsrepl.jsgetsSCOPE_DENIEDunless the selected host domain itself declares arepl.jsmethod (the@manifestdomain never does). The code runs in a worker on the board origin whose response CSP forbids all network access; that header is the boundary.
All of them go through the same frame, origin and CSP path as author extensions.
