From b9b3dd51ca5a4d5f0c118408faacc8af4e94d98f Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Tue, 28 Apr 2026 06:44:32 -0400 Subject: [PATCH 1/3] Update checkout and setup versions --- .github/workflows/test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ea2d0e7..ec17496 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -17,9 +17,9 @@ jobs: - ubuntu-latest steps: - name: Checkout branch - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Install Node.js on ${{ matrix.os }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' cache: 'npm' From e28ca2034332aea5c1f25d43c8af2835b5006c40 Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Tue, 28 Apr 2026 06:49:36 -0400 Subject: [PATCH 2/3] Use Node 24 --- .nvmrc | 2 +- package-lock.json | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index e0325e5..a45fd52 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.17.1 +24 diff --git a/package-lock.json b/package-lock.json index 11d72db..2e47fa9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,9 @@ "c8": "7.13.0", "mocha": "10.2.0" }, + "engines": { + "node": "24.x" + }, "peerDependencies": { "@fabric/core": "FabricLabs/fabric#feature/v0.1.0-RC1", "@fabric/http": "FabricLabs/fabric-http#feature/v0.1.0-RC1", From c1e5ee6ddad4ab9f1a69b868929783df3c442df3 Mon Sep 17 00:00:00 2001 From: Eric Martindale Date: Tue, 28 Apr 2026 07:00:38 -0400 Subject: [PATCH 3/3] Implement basic Bridge component --- README.md | 28 ++++- components/FabricBridge.js | 144 ------------------------- package-lock.json | 215 +++++++++++++++++++++++++++++++++++-- package.json | 16 ++- 4 files changed, 248 insertions(+), 155 deletions(-) delete mode 100644 components/FabricBridge.js diff --git a/README.md b/README.md index 94dbe74..c006f9b 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,39 @@ # `@fabric/react` Fabric integration for React applications. +Install peers (no Hub package required — Hub can depend on this library later): + +```sh +npm install @fabric/react @fabric/core @fabric/http react react-dom semantic-ui-react +``` + +## Platform behavior + +**`Bridge`** is a React class in this package. It uses `@fabric/http` **`Remote`** for the WebSocket client, `@fabric/core` **`Message`** / **`Key`** for wire signing, keeps a **JSON-Patch**–ready **`globalState`**, and exposes the same **static `Bridge.fabric`** helpers (formatting, envelopes, peer identity, safe logging, message type constants) as named exports from the package root. + +WebRTC mesh behavior from the production Hub UI is **not** ported yet: `initializeWebRTC`, `reconnectWebRTC`, and related methods are stubs or no-ops until implemented here. Chat submission (`submitChatMessage`) is stubbed until the full identity pipeline is wired. + +```jsx +import { Bridge, formatSatsDisplay, createEnvelope } from '@fabric/react'; + +formatSatsDisplay(50_000); +Bridge.fabric.format.formatSatsDisplay(50_000); +createEnvelope('Demo', { hello: 'world' }); +``` + ## Quick Start -```js -import React from 'react'; + +```jsx +import React, { Component } from 'react'; import { Container, Header } from 'semantic-ui-react'; +import { Bridge } from '@fabric/react'; + class App extends Component { render () { return ( +

Hello, world!

diff --git a/components/FabricBridge.js b/components/FabricBridge.js deleted file mode 100644 index d6cc2a4..0000000 --- a/components/FabricBridge.js +++ /dev/null @@ -1,144 +0,0 @@ -const React = require('react'); - -// State -// import * as defaults from '../settings/state'; - -// Dependencies -const merge = require('lodash.merge'); -// import FabricComponent from '../types/component'; - -// Components -const { - Button, - Card, - Feed, - Icon, - Label -} = require('semantic-ui-react'); - -// Fabric Types -// import * as Store from '@fabric/core/types/store'; -// import * as Worker from '@fabric/core/types/worker'; -const Remote = require('@fabric/http/types/remote'); - -class FabricBridge extends React.Component { - constructor (props) { - super(props); - - this.settings = Object.assign({ - host: 'localhost', - port: 9999, - secure: false - }, props); - - this.state = merge({ - integrity: 'sha256-deadbeefbabe', - status: 'disconnected', - messages: [], - meta: { - messages: { - count: 0 - } - } - }, this.settings); - - console.log('bridge settings:', this.settings); - - this.remote = new Remote({ - host: this.settings.host, - port: this.settings.port, - secure: this.settings.secure - }); - - /* this.agent = new Worker({ - service: main, - settings: settings - }); */ - - return this; - } - - _handleRemoteMessage (message) { - console.log('Remote message:', message); - this._syncState(); - } - - _handleRemoteError (error) { - console.log('Remote error:', error); - } - - _syncState () { - this.setState({ - status: this.remote._state.status, - messages: this.remote._state.messages, - meta: this.remote._state.meta - }); - } - - componentDidMount () { - console.log('bridge mounted! starting...'); - // this.agent.executeMethod('connect'); - // this.process.executeMethod('connect'); - this.start(); - } - - connect () { - this._syncState(); - this.remote.connect(); - this._syncState(); - } - - executeMethod (name, params) { - return this.remote.executeMethod(name, params); - } - - ping () { - this.remote.ping(); - } - - render () { - return ( - <> - - - - - - - Bridge - - - - {this.state.messages.map((message, i) => { - return ( - - -
{JSON.stringify(message, null, ' ')}
-
-
- ); - })} -
-
- - - - -
- - ); - } - - async send (message) { - return this.remote.send(message); - } - - async start () { - this.remote.on('ready', this.props.remoteReady.bind(this)); - this.remote.on('message', this._handleRemoteMessage.bind(this)); - this.remote.on('error', this._handleRemoteError.bind(this)); - this.connect(); - } -} - -module.exports = FabricBridge; diff --git a/package-lock.json b/package-lock.json index 2e47fa9..15f2693 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "license": "MIT", "dependencies": { "elliptic": "6.5.4", + "fast-json-patch": "^3.1.1", + "lodash.merge": "^4.6.2", "rollup": "2.72.1" }, "devDependencies": { @@ -26,8 +28,9 @@ "peerDependencies": { "@fabric/core": "FabricLabs/fabric#feature/v0.1.0-RC1", "@fabric/http": "FabricLabs/fabric-http#feature/v0.1.0-RC1", - "react": "17.0.1", - "react-dom": "17.0.1" + "react": "^17.0.1 || ^18.2.0", + "react-dom": "^17.0.1 || ^18.2.0", + "semantic-ui-react": "^2.1.0" } }, "node_modules/@ampproject/remapping": { @@ -1743,6 +1746,16 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", @@ -2088,6 +2101,42 @@ "node": ">=4" } }, + "node_modules/@fluentui/react-component-event-listener": { + "version": "0.63.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-component-event-listener/-/react-component-event-listener-0.63.1.tgz", + "integrity": "sha512-gSMdOh6tI3IJKZFqxfQwbTpskpME0CvxdxGM2tdglmf6ZPVDi0L4+KKIm+2dN8nzb8Ya1A8ZT+Ddq0KmZtwVQg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.4" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/@fluentui/react-component-ref": { + "version": "0.63.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-component-ref/-/react-component-ref-0.63.1.tgz", + "integrity": "sha512-8MkXX4+R3i80msdbD4rFpEB4WWq2UDvGwG386g3ckIWbekdvN9z2kWAd9OXhRGqB7QeOsoAGWocp6gAMCivRlw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.4", + "react-is": "^16.6.3" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/@fluentui/react-component-ref/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT", + "peer": true + }, "node_modules/@gar/promise-retry": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.3.tgz", @@ -2407,6 +2456,17 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@puppeteer/browsers": { "version": "2.13.0", "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz", @@ -2622,6 +2682,21 @@ "@redis/client": "^5.11.0" } }, + "node_modules/@semantic-ui-react/event-stack": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@semantic-ui-react/event-stack/-/event-stack-3.1.3.tgz", + "integrity": "sha512-FdTmJyWvJaYinHrKRsMLDrz4tTMGdFfds299Qory53hBugiDvGC0tEJf+cHsi5igDwWb/CLOgOiChInHwq8URQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "exenv": "^1.2.2", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -4293,6 +4368,16 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -5773,6 +5858,13 @@ "safe-buffer": "^5.1.1" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==", + "license": "BSD-3-Clause", + "peer": true + }, "node_modules/exponential-backoff": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", @@ -5975,8 +6067,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", @@ -7297,6 +7388,13 @@ "node": ">=6" } }, + "node_modules/keyboard-key": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keyboard-key/-/keyboard-key-1.1.0.tgz", + "integrity": "sha512-qkBzPTi3rlAKvX7k0/ub44sqOfXeLc/jcnGGmj5c7BJpU8eDrEVPyhCvNYAaoubbsLm9uGWwQJO1ytQK1a9/dQ==", + "license": "MIT", + "peer": true + }, "node_modules/length-prefixed-stream": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/length-prefixed-stream/-/length-prefixed-stream-1.6.0.tgz", @@ -7404,6 +7502,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT", + "peer": true + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT", + "peer": true + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -7415,8 +7527,7 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", @@ -8804,6 +8915,25 @@ "node": ">=0.4.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT", + "peer": true + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -9078,6 +9208,36 @@ "react": "17.0.1" } }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT", + "peer": true + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT", + "peer": true + }, + "node_modules/react-popper": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "react-fast-compare": "^3.0.1", + "warning": "^4.0.2" + }, + "peerDependencies": { + "@popperjs/core": "^2.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -9450,6 +9610,32 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/semantic-ui-react": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-2.1.5.tgz", + "integrity": "sha512-nIqmmUNpFHfovEb+RI2w3E2/maZQutd8UIWyRjf1SLse+XF51hI559xbz/sLN3O6RpLjr/echLOOXwKCirPy3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.10.5", + "@fluentui/react-component-event-listener": "~0.63.0", + "@fluentui/react-component-ref": "~0.63.0", + "@popperjs/core": "^2.6.0", + "@semantic-ui-react/event-stack": "^3.1.3", + "clsx": "^1.1.1", + "keyboard-key": "^1.1.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "prop-types": "^15.7.2", + "react-is": "^16.8.6 || ^17.0.0 || ^18.0.0", + "react-popper": "^2.3.0", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -9573,6 +9759,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "license": "MIT", + "peer": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10822,6 +11015,16 @@ "node": ">=18" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/watchpack": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", diff --git a/package.json b/package.json index ae68772..a300d57 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,15 @@ "name": "@fabric/react", "version": "0.1.0", "description": "Fabric integration for React", - "main": "types/fabric.js", + "main": "./index.js", "type": "module", + "exports": { + ".": "./index.js", + "./Bridge": "./components/Bridge.js", + "./components/Bridge": "./components/Bridge.js", + "./functions/*": "./functions/*", + "./constants/*": "./constants/*" + }, "scripts": { "build": "node scripts/build.js", "build:coverage": "npm run coverage && c8 report --reporter=html", @@ -46,6 +53,8 @@ "homepage": "https://github.com/FabricLabs/fabric-react#readme", "dependencies": { "elliptic": "6.5.4", + "fast-json-patch": "^3.1.1", + "lodash.merge": "^4.6.2", "rollup": "2.72.1" }, "devDependencies": { @@ -59,7 +68,8 @@ "peerDependencies": { "@fabric/core": "FabricLabs/fabric#feature/v0.1.0-RC1", "@fabric/http": "FabricLabs/fabric-http#feature/v0.1.0-RC1", - "react": "17.0.1", - "react-dom": "17.0.1" + "react": "^17.0.1 || ^18.2.0", + "react-dom": "^17.0.1 || ^18.2.0", + "semantic-ui-react": "^2.1.0" } }