From ad0bed69ba30456b6823e0260b58bdc31d53ff22 Mon Sep 17 00:00:00 2001 From: Sasha <118575614+weboko@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:37:49 +0200 Subject: [PATCH] feat: add waku/react package and make it compatible with React frameworks (#2656) * chore: add waku/react package * fix check * remove not needed logic from waku/react package * make it compatible with expo/next * add to release please * remove tests --- .release-please-manifest.json | 3 +- package-lock.json | 493 +++++++++++++++++++++++++++- package.json | 3 +- packages/react/.mocharc.cjs | 26 ++ packages/react/CHANGELOG.md | 11 + packages/react/package.json | 99 ++++++ packages/react/rollup.config.js | 99 ++++++ packages/react/src/WakuProvider.tsx | 57 ++++ packages/react/src/index.ts | 2 + packages/react/src/types.ts | 16 + packages/react/src/useCreateWaku.ts | 63 ++++ packages/react/tsconfig.dev.json | 6 + packages/react/tsconfig.json | 12 + packages/react/typedoc.json | 4 + release-please-config.json | 3 +- 15 files changed, 893 insertions(+), 4 deletions(-) create mode 100644 packages/react/.mocharc.cjs create mode 100644 packages/react/CHANGELOG.md create mode 100644 packages/react/package.json create mode 100644 packages/react/rollup.config.js create mode 100644 packages/react/src/WakuProvider.tsx create mode 100644 packages/react/src/index.ts create mode 100644 packages/react/src/types.ts create mode 100644 packages/react/src/useCreateWaku.ts create mode 100644 packages/react/tsconfig.dev.json create mode 100644 packages/react/tsconfig.json create mode 100644 packages/react/typedoc.json diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7ef6042c11..288bdb7fbc 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -9,5 +9,6 @@ "packages/sdk": "0.0.35", "packages/discovery": "0.0.12", "packages/sds": "0.0.7", - "packages/rln": "0.1.9" + "packages/rln": "0.1.9", + "packages/react": "0.0.7" } diff --git a/package-lock.json b/package-lock.json index baef050b98..370aa3b75a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,8 @@ "packages/tests", "packages/reliability-tests", "packages/browser-tests", - "packages/build-utils" + "packages/build-utils", + "packages/react" ], "devDependencies": { "@size-limit/preset-big-lib": "^11.0.2", @@ -4993,6 +4994,29 @@ } } }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/pluginutils": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", @@ -6780,6 +6804,13 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "license": "MIT" }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", @@ -6794,6 +6825,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/react": { + "version": "18.3.24", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", + "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -7585,6 +7627,10 @@ "resolved": "packages/proto", "link": true }, + "node_modules/@waku/react": { + "resolved": "packages/react", + "link": true + }, "node_modules/@waku/relay": { "resolved": "packages/relay", "link": true @@ -12312,6 +12358,13 @@ "node": ">=18" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, "node_modules/custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", @@ -14717,6 +14770,19 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -14777,6 +14843,16 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-simple-import-sort": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-10.0.0.tgz", + "integrity": "sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -28195,6 +28271,19 @@ "node": ">=0.10.0" } }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -29447,6 +29536,53 @@ "node": ">= 4.0.0" } }, + "node_modules/rollup-plugin-typescript2": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.36.0.tgz", + "integrity": "sha512-NB2CSQDxSe9+Oe2ahZbf+B4bh7pHwjV5L+RSYpCu7Q5ROuN94F9b6ioWwKfz3ueL3KTtmX4o2MUH2cgHDIEUsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^4.1.2", + "find-cache-dir": "^3.3.2", + "fs-extra": "^10.0.0", + "semver": "^7.5.4", + "tslib": "^2.6.2" + }, + "peerDependencies": { + "rollup": ">=1.26.3", + "typescript": ">=2.4.0" + } + }, + "node_modules/rollup-plugin-typescript2/node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/rollup-plugin-typescript2/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -30867,6 +31003,13 @@ "npm": ">= 3.0.0" } }, + "node_modules/smob": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", + "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", + "dev": true, + "license": "MIT" + }, "node_modules/socket.io": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", @@ -35256,6 +35399,354 @@ "node": ">=22" } }, + "packages/react": { + "name": "@waku/react", + "version": "0.0.7", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@waku/interfaces": "0.0.34", + "@waku/sdk": "0.0.35", + "@waku/utils": "0.0.27" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-json": "^6.0.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-terser": "^0.4.4", + "@types/chai": "^4.3.11", + "@types/mocha": "^10.0.9", + "@types/react": "^18.0.28", + "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/parser": "^7.1.1", + "@waku/build-utils": "*", + "chai": "^5.1.1", + "cspell": "^8.6.1", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-simple-import-sort": "^10.0.0", + "mocha": "^10.7.3", + "npm-run-all": "^4.1.5", + "rollup": "^4.12.0", + "rollup-plugin-typescript2": "^0.36.0" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "packages/react/node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/react/node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/react/node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/react/node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/react/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/react/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "packages/react/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "packages/react/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "packages/react/node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "packages/react/node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "packages/react/node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "packages/react/node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "packages/react/node_modules/eslint-config-prettier": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.2.tgz", + "integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "packages/react/node_modules/eslint-plugin-prettier": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.5.tgz", + "integrity": "sha512-9Ni+xgemM2IWLq6aXEpP2+V/V30GeA/46Ar629vcMqVPodFFWC9skHu/D1phvuqtS8bJCFnNf01/qcmqYEwNfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "packages/react/node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "packages/react/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/react/node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "packages/relay": { "name": "@waku/relay", "version": "0.0.22", diff --git a/package.json b/package.json index eb8f1aa57f..bd29d5bc00 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "packages/tests", "packages/reliability-tests", "packages/browser-tests", - "packages/build-utils" + "packages/build-utils", + "packages/react" ], "scripts": { "prepare": "husky", diff --git a/packages/react/.mocharc.cjs b/packages/react/.mocharc.cjs new file mode 100644 index 0000000000..a2d47774f0 --- /dev/null +++ b/packages/react/.mocharc.cjs @@ -0,0 +1,26 @@ +const config = { + extension: ['ts', 'tsx'], + spec: 'src/**/*.spec.{ts,tsx}', + require: ['ts-node/register', 'isomorphic-fetch'], + loader: 'ts-node/esm', + 'node-option': [ + 'experimental-specifier-resolution=node', + 'loader=ts-node/esm' + ], + exit: true, + retries: 4 +}; + +if (process.env.CI) { + console.log("Running tests in parallel"); + config.parallel = true; + config.jobs = 6; + console.log("Activating allure reporting"); + config.reporter = 'mocha-multi-reporters'; + config.reporterOptions = { + configFile: '.mocha.reporters.json' + }; +} else { + console.log("Running tests serially. To enable parallel execution update mocha config"); +} +module.exports = config; diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md new file mode 100644 index 0000000000..9572b7e333 --- /dev/null +++ b/packages/react/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.0.7] - Unreleased + +### Added +- Initial release with React hooks and components for js-waku diff --git a/packages/react/package.json b/packages/react/package.json new file mode 100644 index 0000000000..ca0da6f168 --- /dev/null +++ b/packages/react/package.json @@ -0,0 +1,99 @@ +{ + "name": "@waku/react", + "version": "0.0.7", + "description": "React hooks and components to use js-waku", + "type": "module", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.mjs", + "umd:main": "dist/index.umd.js", + "unpkg": "dist/index.umd.js", + "source": "src/index.ts", + "types": "dist/index.d.ts", + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.esm.mjs", + "require": "./dist/index.cjs.js" + } + }, + "author": "Waku Team", + "homepage": "https://github.com/waku-org/js-waku/tree/master/packages/react#readme", + "repository": { + "type": "git", + "url": "https://github.com/waku-org/js-waku.git" + }, + "bugs": { + "url": "https://github.com/waku-org/js-waku/issues" + }, + "license": "MIT OR Apache-2.0", + "keywords": [ + "waku", + "decentralized", + "secure", + "communication", + "web3", + "ethereum", + "dapps", + "privacy", + "react" + ], + "scripts": { + "build": "rollup --config rollup.config.js", + "fix": "run-s fix:*", + "fix:lint": "eslint src *.js --fix", + "check": "run-s check:*", + "check:tsc": "tsc -p tsconfig.dev.json", + "check:lint": "eslint src *.js", + "check:spelling": "cspell \"{README.md,src/**/*.ts,src/**/*.tsx}\"", + "watch:build": "tsc -p tsconfig.json -w", + "prepublish": "npm run build", + "reset-hard": "git clean -dfx -e .idea && git reset --hard && npm i && npm run build" + }, + "engines": { + "node": ">=22" + }, + "dependencies": { + "@waku/interfaces": "0.0.34", + "@waku/sdk": "0.0.35", + "@waku/utils": "0.0.27" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-json": "^6.0.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@types/chai": "^4.3.11", + "@types/mocha": "^10.0.9", + "@types/react": "^18.0.28", + "@typescript-eslint/eslint-plugin": "^7.1.1", + "@typescript-eslint/parser": "^7.1.1", + "@waku/build-utils": "*", + "chai": "^5.1.1", + "cspell": "^8.6.1", + "eslint": "^8.34.0", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-simple-import-sort": "^10.0.0", + "mocha": "^10.7.3", + "npm-run-all": "^4.1.5", + "rollup": "^4.12.0", + "@rollup/plugin-terser": "^0.4.4", + "rollup-plugin-typescript2": "^0.36.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + }, + "files": [ + "dist", + "bundle", + "src/**/*.ts", + "src/**/*.tsx", + "!**/*.spec.*", + "!**/*.json", + "CHANGELOG.md", + "LICENSE", + "README.md" + ] +} diff --git a/packages/react/rollup.config.js b/packages/react/rollup.config.js new file mode 100644 index 0000000000..fae00960ce --- /dev/null +++ b/packages/react/rollup.config.js @@ -0,0 +1,99 @@ +import commonjs from "@rollup/plugin-commonjs"; +import json from "@rollup/plugin-json"; +import { nodeResolve } from "@rollup/plugin-node-resolve"; +import terser from "@rollup/plugin-terser"; +import typescript from "rollup-plugin-typescript2"; + +const input = "src/index.ts"; + +const external = [ + "react", + "react-dom", + "@waku/interfaces", + "@waku/sdk", + "@waku/utils" +]; + +export default [ + { + input, + output: { + file: "dist/index.esm.mjs", + format: "esm", + sourcemap: true + }, + external, + plugins: [ + nodeResolve({ + browser: true, + preferBuiltins: false + }), + commonjs(), + json(), + typescript({ + tsconfig: "./tsconfig.json", + useTsconfigDeclarationDir: true + }) + ] + }, + { + input, + output: { + file: "dist/index.cjs.js", + format: "cjs", + sourcemap: true, + exports: "named" + }, + external, + plugins: [ + nodeResolve({ + browser: true, + preferBuiltins: false + }), + commonjs(), + json(), + typescript({ + tsconfig: "./tsconfig.json", + tsconfigOverride: { + compilerOptions: { + declaration: false + } + } + }) + ] + }, + { + input, + output: { + file: "dist/index.umd.js", + format: "umd", + name: "WakuReact", + sourcemap: true, + globals: { + react: "React", + "react-dom": "ReactDOM", + "@waku/interfaces": "WakuInterfaces", + "@waku/sdk": "WakuSDK", + "@waku/utils": "WakuUtils" + } + }, + external, + plugins: [ + nodeResolve({ + browser: true, + preferBuiltins: false + }), + commonjs(), + json(), + typescript({ + tsconfig: "./tsconfig.json", + tsconfigOverride: { + compilerOptions: { + declaration: false + } + } + }), + terser() + ] + } +]; diff --git a/packages/react/src/WakuProvider.tsx b/packages/react/src/WakuProvider.tsx new file mode 100644 index 0000000000..557eb9be0c --- /dev/null +++ b/packages/react/src/WakuProvider.tsx @@ -0,0 +1,57 @@ +"use client"; +import type { CreateNodeOptions, IWaku, LightNode } from "@waku/interfaces"; +import * as React from "react"; + +import type { CreateNodeResult, ReactChildrenProps } from "./types.js"; +import { useCreateLightNode } from "./useCreateWaku.js"; + +type WakuContextType = CreateNodeResult; + +const WakuContext = React.createContext>({ + node: undefined, + isLoading: false, + error: undefined +}); + +/** + * Hook to retrieve Waku node from Context. By default generic Waku type will be used. + * @example + * const { node, isLoading, error } = useWaku(); + * @example + * const { node, isLoading, error } = useWaku(); + * @example + * const { node, isLoading, error } = useWaku(); + * @example + * const { node, isLoading, error } = useWaku(); + * @returns WakuContext + */ +export const useWaku = (): WakuContextType => + React.useContext(WakuContext) as WakuContextType; + +type ProviderProps = ReactChildrenProps & { options: T }; + +/** + * Provider for creating Waku node based on options passed. + * @example + * const App = (props) => ( + * + * + * + * ); + * const Component = (props) => { + * const { node, isLoading, error } = useWaku(); + * ... + * }; + * @param {Object} props - options to create a node and other React props + * @param {CreateNodeOptions} props.options - optional options for creating Light Node + * @returns React Light Node provider component + */ +export const WakuProvider = ( + props: ProviderProps +): React.ReactElement => { + const result = useCreateLightNode(props.options); + + return ( + {props.children} + ); +}; diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts new file mode 100644 index 0000000000..cf41fa9f5f --- /dev/null +++ b/packages/react/src/index.ts @@ -0,0 +1,2 @@ +export type { CreateNodeOptions } from "./types.js"; +export { WakuProvider, useWaku } from "./WakuProvider.js"; diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts new file mode 100644 index 0000000000..2f9313b847 --- /dev/null +++ b/packages/react/src/types.ts @@ -0,0 +1,16 @@ +import type { IWaku } from "@waku/interfaces"; +import type * as React from "react"; +export type { CreateNodeOptions, AutoSharding } from "@waku/interfaces"; + +type HookState = { + isLoading: boolean; + error: undefined | string; +}; + +export type CreateNodeResult = HookState & { + node: undefined | T; +}; + +export type ReactChildrenProps = { + children?: React.ReactNode; +}; diff --git a/packages/react/src/useCreateWaku.ts b/packages/react/src/useCreateWaku.ts new file mode 100644 index 0000000000..7e99d1683a --- /dev/null +++ b/packages/react/src/useCreateWaku.ts @@ -0,0 +1,63 @@ +"use client"; +import type { CreateNodeOptions, IWaku, LightNode } from "@waku/interfaces"; +import { createLightNode } from "@waku/sdk"; +import * as React from "react"; + +import type { CreateNodeResult } from "./types.js"; + +type NodeFactory> = (options?: T) => Promise; + +type CreateNodeParams> = T & { + factory: NodeFactory; +}; + +const useCreateNode = >( + params: CreateNodeParams +): CreateNodeResult => { + const { factory, ...options } = params; + + const [node, setNode] = React.useState(undefined); + const [isLoading, setLoading] = React.useState(true); + const [error, setError] = React.useState(undefined); + + React.useEffect(() => { + let cancelled = false; + setLoading(true); + + factory(options as T) + .then(async (node) => { + if (cancelled) { + return; + } + + await node.start(); + await node.waitForPeers(); + + setNode(node); + setLoading(false); + }) + .catch((err) => { + setLoading(false); + setError(`Failed at creating node: ${err?.message || "no message"}`); + }); + + return () => { + cancelled = true; + }; + }, []); + + return { + node, + error, + isLoading + }; +}; + +export const useCreateLightNode = ( + params?: CreateNodeOptions +): CreateNodeResult => { + return useCreateNode({ + ...params, + factory: createLightNode + }); +}; diff --git a/packages/react/tsconfig.dev.json b/packages/react/tsconfig.dev.json new file mode 100644 index 0000000000..7820948c64 --- /dev/null +++ b/packages/react/tsconfig.dev.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.dev", + "compilerOptions": { + "jsx": "react" + } +} diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json new file mode 100644 index 0000000000..cbac7ad67b --- /dev/null +++ b/packages/react/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "dist/", + "rootDir": "src", + "tsBuildInfoFile": "dist/.tsbuildinfo", + "declarationDir": "dist", + "jsx": "react" + }, + "include": ["src", "*.js"], + "exclude": ["src/**/*.spec.ts", "src/**/*.spec.tsx", "src/test_utils"] +} diff --git a/packages/react/typedoc.json b/packages/react/typedoc.json new file mode 100644 index 0000000000..f593f276c2 --- /dev/null +++ b/packages/react/typedoc.json @@ -0,0 +1,4 @@ +{ + "extends": ["../../typedoc.base.json"], + "entryPoints": ["src/index.ts"] +} diff --git a/release-please-config.json b/release-please-config.json index 3c9e34aa2f..622f9b6933 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -23,6 +23,7 @@ "packages/relay": {}, "packages/discovery": {}, "packages/sds": {}, - "packages/rln": {} + "packages/rln": {}, + "packages/react": {} } } \ No newline at end of file