chore: add waku/react package

This commit is contained in:
Sasha 2025-09-29 23:41:02 +02:00
parent 79dd001b1f
commit 81a9abd13a
No known key found for this signature in database
18 changed files with 1264 additions and 2 deletions

493
package-lock.json generated
View File

@ -21,7 +21,8 @@
"packages/reliability-tests",
"packages/headless-tests",
"packages/browser-tests",
"packages/build-utils"
"packages/build-utils",
"packages/react"
],
"devDependencies": {
"@size-limit/preset-big-lib": "^11.0.2",
@ -5841,6 +5842,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",
@ -7626,6 +7650,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",
@ -7640,6 +7671,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",
@ -8385,6 +8427,10 @@
"resolved": "packages/proto",
"link": true
},
"node_modules/@waku/react": {
"resolved": "packages/react",
"link": true
},
"node_modules/@waku/relay": {
"resolved": "packages/relay",
"link": true
@ -13643,6 +13689,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",
@ -16086,6 +16139,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",
@ -16146,6 +16212,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",
@ -29794,6 +29870,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",
@ -31068,6 +31157,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",
@ -32719,6 +32855,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",
@ -37373,6 +37516,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"
}
},
"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",

View File

@ -18,7 +18,8 @@
"packages/reliability-tests",
"packages/headless-tests",
"packages/browser-tests",
"packages/build-utils"
"packages/build-utils",
"packages/react"
],
"scripts": {
"prepare": "husky",

View File

@ -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;

View File

@ -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

102
packages/react/package.json Normal file
View File

@ -0,0 +1,102 @@
{
"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}\"",
"test": "NODE_ENV=test run-s test:*",
"test:node": "NODE_ENV=test TS_NODE_PROJECT=./tsconfig.dev.json mocha",
"watch:build": "tsc -p tsconfig.json -w",
"watch:test": "mocha --watch",
"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"
},
"files": [
"dist",
"bundle",
"src/**/*.ts",
"src/**/*.tsx",
"!**/*.spec.*",
"!**/*.json",
"CHANGELOG.md",
"LICENSE",
"README.md"
]
}

View File

@ -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()
]
}
];

View File

@ -0,0 +1,53 @@
import React from "react";
import type { ContentPair, ReactChildrenProps } from "./types.js";
import { useCreateContentPair } from "./useCreatContentPair.js";
type ContentPairContextType = Partial<ContentPair>;
const ContentPairContext = React.createContext<ContentPairContextType>({
decoder: undefined,
encoder: undefined
});
/**
* Hook to retrieve Encoder/Decoder pair from Context.
* @example
* const { encoder, decoder } = useContentPair();
* @returns {Object} { encoder, decoder }
*/
export const useContentPair = (): ContentPairContextType =>
React.useContext(ContentPairContext);
type ContentPairProviderProps = ReactChildrenProps & {
contentTopic: string;
ephemeral?: boolean;
};
/**
* Provider for creating Encoder/Decoder pair based on contentTopic
* @example
* const App = (props) => (
* <ContentPairProvider contentTopic="/toy-chat/2/huilong/proto">
* <Component />
* </ContentPairProvider>
* );
* const Component = (props) => {
* const { encoder, decoder } = useContentPair();
* ...
* };
* @param {string} contentTopic - content topic for configuring the pair
* @param {boolean} ephemeral - flag to set messages ephemeral according to RFC https://rfc.vac.dev/spec/14/
* @returns React ContentPair Provider component
*/
export const ContentPairProvider: React.FunctionComponent<
ContentPairProviderProps
> = (props) => {
const result = useCreateContentPair(props.contentTopic, props.ephemeral);
return (
<ContentPairContext.Provider value={result}>
{props.children}
</ContentPairContext.Provider>
);
};

View File

@ -0,0 +1,64 @@
import type { CreateNodeOptions, IWaku } from "@waku/interfaces";
import React from "react";
import type {
BootstrapNodeOptions,
CreateNodeResult,
ReactChildrenProps
} from "./types.js";
import { useCreateLightNode } from "./useCreateWaku.js";
type WakuContextType<T extends IWaku> = CreateNodeResult<T>;
const WakuContext = React.createContext<WakuContextType<IWaku>>({
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<LightNode>();
* @example
* const { node, isLoading, error } = useWaku<RelayNode>();
* @example
* const { node, isLoading, error } = useWaku<FullNode>();
* @example
* const { node, isLoading, error } = useWaku();
* @returns WakuContext
*/
export const useWaku = <T extends IWaku>(): WakuContextType<T> =>
React.useContext(WakuContext) as WakuContextType<T>;
type ProviderProps<T> = ReactChildrenProps & BootstrapNodeOptions<T>;
/**
* Provider for creating Light Node based on options passed.
* @example
* const App = (props) => (
* <LightNodeProvider options={{...}}>
* <Component />
* </LightNodeProvider>
* );
* const Component = (props) => {
* const { node, isLoading, error } = useWaku<LightNode>();
* ...
* };
* @param {Object} props - options to create a node and other React props
* @param {CreateNodeOptions} props.options - optional options for creating Light Node
* @param {Protocols} props.protocols - optional protocols list to initiate node with
* @returns React Light Node provider component
*/
export const LightNodeProvider: React.FunctionComponent<
ProviderProps<CreateNodeOptions>
> = (props) => {
const result = useCreateLightNode({
options: props.options,
protocols: props.protocols
});
return (
<WakuContext.Provider value={result}>{props.children}</WakuContext.Provider>
);
};

View File

@ -0,0 +1,8 @@
export { ContentPairProvider, useContentPair } from "./ContentPairProvider.js";
export { CreateNodeOptions } from "./types.js";
export { useCreateContentPair } from "./useCreatContentPair.js";
export { useCreateLightNode } from "./useCreateWaku.js";
export { useFilterMessages } from "./useFilterMessages.js";
export { useLightPush } from "./useLightPush.js";
export { useStoreMessages } from "./useStoreMessages.js";
export { LightNodeProvider, useWaku } from "./WakuProvider.js";

View File

@ -0,0 +1,31 @@
import type {
IDecodedMessage,
IDecoder,
IEncoder,
IWaku,
Protocols
} from "@waku/interfaces";
export type { CreateNodeOptions, AutoSharding } from "@waku/interfaces";
export type HookState = {
isLoading: boolean;
error: undefined | string;
};
export type CreateNodeResult<T extends IWaku> = HookState & {
node: undefined | T;
};
export type BootstrapNodeOptions<T = Record<string, never>> = {
options?: T;
protocols?: Protocols[];
};
export type ContentPair = {
encoder: IEncoder;
decoder: IDecoder<IDecodedMessage>;
};
export type ReactChildrenProps = {
children?: React.ReactNode;
};

View File

@ -0,0 +1,59 @@
import type {
AutoSharding,
IDecodedMessage,
IDecoder,
IEncoder
} from "@waku/interfaces";
import { createDecoder, createEncoder } from "@waku/sdk";
import { createRoutingInfo } from "@waku/utils";
import React from "react";
import type { ContentPair } from "./types.js";
const defaultNetworkConfig: AutoSharding = {
clusterId: 0,
numShardsInCluster: 8
};
/**
* Creates Encoder / Decoder pair for a given contentTopic.
* @param {string} contentTopic - topic to orient to
* @param {boolean} ephemeral - makes messages ephemeral, default to false
* @param {AutoSharding} networkConfig - optional network config, defaults to cluster 0 with 8 shards
* @returns {Object} Encoder / Decoder pair
*/
export const useCreateContentPair = (
contentTopic: string,
ephemeral = false,
networkConfig: AutoSharding = defaultNetworkConfig
): ContentPair => {
const routingInfo = React.useMemo(
() => createRoutingInfo(networkConfig, { contentTopic }),
[contentTopic, networkConfig.clusterId, networkConfig.numShardsInCluster]
);
const [encoder, setEncoder] = React.useState<IEncoder>(
createEncoder({ contentTopic, ephemeral, routingInfo })
);
const [decoder, setDecoder] = React.useState<IDecoder<IDecodedMessage>>(
createDecoder(contentTopic, routingInfo)
);
React.useEffect(() => {
const newRoutingInfo = createRoutingInfo(networkConfig, { contentTopic });
setEncoder(
createEncoder({ contentTopic, ephemeral, routingInfo: newRoutingInfo })
);
setDecoder(createDecoder(contentTopic, newRoutingInfo));
}, [
contentTopic,
ephemeral,
networkConfig.clusterId,
networkConfig.numShardsInCluster
]);
return {
encoder,
decoder
};
};

View File

@ -0,0 +1,65 @@
import type { CreateNodeOptions, IWaku, LightNode } from "@waku/interfaces";
import { createLightNode, waitForRemotePeer } from "@waku/sdk";
import React from "react";
import type { BootstrapNodeOptions, CreateNodeResult } from "./types.js";
type NodeFactory<N, T = Record<string, never>> = (options?: T) => Promise<N>;
type CreateNodeParams<
N extends IWaku,
T = Record<string, never>
> = BootstrapNodeOptions<T> & {
factory: NodeFactory<N, T>;
};
const useCreateNode = <N extends IWaku, T = Record<string, never>>(
params: CreateNodeParams<N, T>
): CreateNodeResult<N> => {
const { factory, options, protocols = [] } = params;
const [node, setNode] = React.useState<N | undefined>(undefined);
const [isLoading, setLoading] = React.useState<boolean>(true);
const [error, setError] = React.useState<undefined | string>(undefined);
React.useEffect(() => {
let cancelled = false;
setLoading(true);
factory(options)
.then(async (node) => {
if (cancelled) {
return;
}
await node.start();
await waitForRemotePeer(node, protocols);
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?: BootstrapNodeOptions<CreateNodeOptions>
): CreateNodeResult<LightNode> => {
return useCreateNode<LightNode, CreateNodeOptions>({
...params,
factory: createLightNode
});
};

View File

@ -0,0 +1,89 @@
import type {
IDecodedMessage,
IDecoder,
IFilter,
IWaku
} from "@waku/interfaces";
import { Logger } from "@waku/utils";
import React from "react";
import type { HookState } from "./types.js";
const log = new Logger("react:useFilterMessages");
type AbstractFilterNode = IWaku & {
filter: IFilter;
};
type UseFilterMessagesParams = {
node: undefined | AbstractFilterNode;
decoder: undefined | IDecoder<IDecodedMessage>;
};
type UseFilterMessagesResult = HookState & {
messages: IDecodedMessage[];
};
/**
* Returns messages from Filter subscription and keeps them up to date
* @example
* const { isLoading, error, message } = useFilterMessages({node, decoder});
* @param {Object} node - node that implements Filter, hook does nothing if undefined
* @param {Object} decoder - decoder to use for subscribing, hook does nothing if undefined
* @returns {Object} hook state (isLoading, error) and messages array
*/
export const useFilterMessages = (
params: UseFilterMessagesParams
): UseFilterMessagesResult => {
const { node, decoder } = params;
const [error, setError] = React.useState<undefined | string>(undefined);
const [isLoading, setLoading] = React.useState<boolean>(false);
const [messages, setMessage] = React.useState<IDecodedMessage[]>([]);
const pushMessage = React.useCallback(
(message: IDecodedMessage): void => {
if (!message) {
return;
}
setMessage((prev) => [...prev, message]);
},
[setMessage]
);
React.useEffect(() => {
if (!node || !decoder) {
return;
}
setLoading(true);
node.filter
.subscribe([decoder], pushMessage)
.then((success) => {
setLoading(false);
if (!success) {
setError("Failed to subscribe to filter");
}
})
.catch((err) => {
setLoading(false);
setError(
`Failed to subscribe to filter: ${err?.message || "no message"}`
);
});
return () => {
node.filter.unsubscribe([decoder]).catch((err) => {
log.error("Failed to unsubscribe:", err);
});
};
}, [node, decoder, pushMessage, setError, setLoading]);
return {
error,
messages,
isLoading
};
};

View File

@ -0,0 +1,44 @@
import type {
IEncoder,
ILightPush,
IMessage,
IWaku,
LightPushSDKResult
} from "@waku/interfaces";
import React from "react";
type AbstractLightPushNode = IWaku & {
lightPush: ILightPush;
};
type UseLightPushParams = {
encoder: undefined | IEncoder;
node: undefined | AbstractLightPushNode;
};
type PushFn = (message: IMessage) => Promise<LightPushSDKResult>;
type UseLightPushResult = {
push?: undefined | PushFn;
};
export const useLightPush = (
params: UseLightPushParams
): UseLightPushResult => {
const { node, encoder } = params;
const push = React.useCallback<PushFn>(
(message) => {
return node!.lightPush.send(encoder as IEncoder, message);
},
[node, encoder]
);
if (!node && !encoder) {
return {};
}
return {
push
};
};

View File

@ -0,0 +1,101 @@
import type {
IDecodedMessage,
IDecoder,
IStore,
IWaku,
QueryRequestParams
} from "@waku/interfaces";
import React from "react";
import type { HookState } from "./types.js";
type AbstractStoreNode = IWaku & {
store: IStore;
};
type UseStoreMessagesParams = {
node: undefined | AbstractStoreNode;
decoder: undefined | IDecoder<IDecodedMessage>;
options: QueryRequestParams;
};
type UseStoreMessagesResult = HookState & {
messages: IDecodedMessage[];
};
/**
* Hook for retrieving messages from Store protocol based on options
* @example
* const { isLoading, error, messages } = useStoreMessages({node, decoder, options});
* @param {Object} node - node that implement Store, hook does nothing if undefined
* @param {Object} decoder - decoder to use for getting messages, hook does nothing if undefined
* @param {QueryRequestParams} options - options to initiate query to get messages
* @returns {Object} hook state (isLoading, error) and messages array
*/
export const useStoreMessages = (
params: UseStoreMessagesParams
): UseStoreMessagesResult => {
const { node, decoder, options } = params;
const [error, setError] = React.useState<undefined | string>(undefined);
const [isLoading, setLoading] = React.useState<boolean>(false);
const [messages, setMessage] = React.useState<IDecodedMessage[]>([]);
const pushMessage = React.useCallback(
(newMessages: IDecodedMessage[]): void => {
if (!newMessages || !newMessages.length) {
return;
}
setMessage((prev) => [...prev, ...newMessages]);
},
[setMessage]
);
React.useEffect(() => {
if (!node || !decoder) {
return;
}
let cancelled = false;
setLoading(true);
Promise.resolve()
.then(async () => {
for await (const promises of node.store.queryGenerator(
[decoder],
options
)) {
if (cancelled) {
return;
}
const messagesRaw = await Promise.all(promises);
const filteredMessages = messagesRaw.filter(
(v): v is IDecodedMessage => !!v
);
pushMessage(filteredMessages);
}
setLoading(false);
})
.catch((err) => {
setLoading(false);
setError(
`Failed to query messages from store: ${err?.message || "no message"}`
);
});
return () => {
cancelled = true;
};
// TODO: missing dependency on options, it will prevent consecutive update if options change
}, [node, decoder, pushMessage, setError, setLoading]);
return {
error,
isLoading,
messages
};
};

View File

@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.dev"
}

View File

@ -0,0 +1,11 @@
{
"extends": "../../tsconfig",
"compilerOptions": {
"outDir": "dist/",
"rootDir": "src",
"tsBuildInfoFile": "dist/.tsbuildinfo",
"jsx": "react"
},
"include": ["src", "*.js"],
"exclude": ["src/**/*.spec.ts", "src/**/*.spec.tsx", "src/test_utils"]
}

View File

@ -0,0 +1,4 @@
{
"extends": ["../../typedoc.base.json"],
"entryPoints": ["src/index.ts"]
}