From 6e47735f6388e5d41d7fd43389600eed0c6b8429 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Thu, 12 Mar 2026 22:06:11 -0700 Subject: [PATCH 1/7] Make minikit compatible --- js/examples/nextjs/app/layout.tsx | 5 +- js/examples/nextjs/app/ui.tsx | 5 +- js/examples/nextjs/package.json | 4 +- js/packages/core/src/index.ts | 1 + .../react/src/__tests__/hooks.test.tsx | 1 + js/packages/react/src/hooks/common.ts | 2 + js/packages/react/src/hooks/useIDKitFlow.ts | 19 +- js/packages/react/src/types/common.ts | 2 + .../react/src/widget/IDKitRequestWidget.tsx | 31 ++- pnpm-lock.yaml | 258 ++++++++++++++++++ 10 files changed, 315 insertions(+), 13 deletions(-) diff --git a/js/examples/nextjs/app/layout.tsx b/js/examples/nextjs/app/layout.tsx index 9f1a199a..56451329 100644 --- a/js/examples/nextjs/app/layout.tsx +++ b/js/examples/nextjs/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import type { ReactNode } from "react"; import { ErudaProvider } from "./eruda"; +import { MiniKitProvider } from "@worldcoin/minikit-js/minikit-provider"; import "./globals.css"; export const metadata: Metadata = { @@ -14,7 +15,9 @@ export default function RootLayout({ return ( - {children} + + {children} + ); diff --git a/js/examples/nextjs/app/ui.tsx b/js/examples/nextjs/app/ui.tsx index 193da6fb..6708e759 100644 --- a/js/examples/nextjs/app/ui.tsx +++ b/js/examples/nextjs/app/ui.tsx @@ -284,8 +284,11 @@ export function DemoClient(): ReactElement { rp_context={widgetRpContext} allow_legacy_proofs={true} preset={widgetPreset} - onSuccess={() => {}} + onSuccess={() => { + console.log("success"); + }} handleVerify={async (result) => { + console.log("test"); const verified = await verifyProof(result); setWidgetVerifyResult(verified); }} diff --git a/js/examples/nextjs/package.json b/js/examples/nextjs/package.json index ea5606c5..ef32ceca 100644 --- a/js/examples/nextjs/package.json +++ b/js/examples/nextjs/package.json @@ -9,10 +9,12 @@ }, "dependencies": { "@worldcoin/idkit": "workspace:*", + "@worldcoin/minikit-js": "^1.11.0", "eruda": "^3.4.1", "next": "^15.4.0", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "viem": "^2.47.2" }, "devDependencies": { "@types/react": "^18.3.12", diff --git a/js/packages/core/src/index.ts b/js/packages/core/src/index.ts index 45cd2c7a..70bf9823 100644 --- a/js/packages/core/src/index.ts +++ b/js/packages/core/src/index.ts @@ -62,6 +62,7 @@ export type { IDKitErrorCode } from "./types/result"; // Utilities export { isReactNative, isWeb, isNode } from "./lib/platform"; +export { isInWorldApp } from "./transports/native"; // RP Request Signing (server-side only) export { signRequest } from "./signing"; diff --git a/js/packages/react/src/__tests__/hooks.test.tsx b/js/packages/react/src/__tests__/hooks.test.tsx index 322e462c..64f9aa55 100644 --- a/js/packages/react/src/__tests__/hooks.test.tsx +++ b/js/packages/react/src/__tests__/hooks.test.tsx @@ -24,6 +24,7 @@ vi.mock("@worldcoin/idkit-core", () => ({ MalformedRequest: "malformed_request", UnexpectedResponse: "unexpected_response", }, + isInWorldApp: () => false, })); const baseRpContext = { diff --git a/js/packages/react/src/hooks/common.ts b/js/packages/react/src/hooks/common.ts index 4fff5b14..1c4ddb7d 100644 --- a/js/packages/react/src/hooks/common.ts +++ b/js/packages/react/src/hooks/common.ts @@ -13,6 +13,7 @@ export type HookState = { connectorURI: string | null; result: TResult | null; errorCode: IDKitErrorCodes | null; + isInWorldApp: boolean; }; export function createInitialHookState(): HookState { @@ -22,6 +23,7 @@ export function createInitialHookState(): HookState { connectorURI: null, result: null, errorCode: null, + isInWorldApp: false, }; } diff --git a/js/packages/react/src/hooks/useIDKitFlow.ts b/js/packages/react/src/hooks/useIDKitFlow.ts index 9bf570f7..a92a664c 100644 --- a/js/packages/react/src/hooks/useIDKitFlow.ts +++ b/js/packages/react/src/hooks/useIDKitFlow.ts @@ -1,5 +1,9 @@ -import { useCallback, useEffect, useRef, useState } from "react"; -import { IDKitErrorCodes, type IDKitRequest } from "@worldcoin/idkit-core"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { + IDKitErrorCodes, + isInWorldApp as isInWorldAppCheck, + type IDKitRequest, +} from "@worldcoin/idkit-core"; import type { FlowConfig, IDKitHookResult } from "../types"; import { createInitialHookState, @@ -16,6 +20,8 @@ export function useIDKitFlow( createFlowHandle: () => Promise, config: FlowConfig, ): IDKitHookResult { + const isInWorldApp = useMemo(() => isInWorldAppCheck(), []); + const [state, setState] = useState>( createInitialHookState, ); @@ -49,9 +55,10 @@ export function useIDKitFlow( connectorURI: null, result: null, errorCode: null, + isInWorldApp: isInWorldApp, }; }); - }, []); + }, [isInWorldApp]); useEffect(() => { if (!state.isOpen) { @@ -86,11 +93,12 @@ export function useIDKitFlow( requestId: request.requestId, }); + const connectorURI = isInWorldApp ? null : request.connectorURI; setState((prev) => { - if (prev.connectorURI === request.connectorURI) { + if (prev.connectorURI === connectorURI) { return prev; } - return { ...prev, connectorURI: request.connectorURI }; + return { ...prev, connectorURI }; }); const pollInterval = configRef.current.polling?.interval ?? 1000; @@ -170,5 +178,6 @@ export function useIDKitFlow( result: state.result, errorCode: state.errorCode, isOpen: state.isOpen, + isInWorldApp: isInWorldApp, }; } diff --git a/js/packages/react/src/types/common.ts b/js/packages/react/src/types/common.ts index 657ef399..a05a2530 100644 --- a/js/packages/react/src/types/common.ts +++ b/js/packages/react/src/types/common.ts @@ -20,4 +20,6 @@ export type IDKitHookResult = { result: TResult | null; errorCode: IDKitErrorCodes | null; isOpen: boolean; + /** `true` when running inside World App (mini app context). The widget renders nothing and `handleVerify` is skipped. */ + isInWorldApp: boolean; }; diff --git a/js/packages/react/src/widget/IDKitRequestWidget.tsx b/js/packages/react/src/widget/IDKitRequestWidget.tsx index af7dca7e..d5b647ba 100644 --- a/js/packages/react/src/widget/IDKitRequestWidget.tsx +++ b/js/packages/react/src/widget/IDKitRequestWidget.tsx @@ -99,13 +99,34 @@ export function IDKitRequestWidget({ }); }, [effectiveErrorCode, onError]); - // Auto-close on success + // In World App context there's no UI to render HostAppVerificationState, + // so invoke handleVerify programmatically when the proof arrives. useEffect(() => { - if (isSuccess && autoClose) { - const timer = setTimeout(() => onOpenChange(false), 2500); - return () => clearTimeout(timer); + if (!flow.isInWorldApp || !isHostVerifying || !flow.result || !handleVerify) { + return; } - }, [isSuccess, autoClose, onOpenChange]); + + void Promise.resolve(handleVerify(flow.result)) + .then(() => setHostVerifyResult("passed")) + .catch(() => setHostVerifyResult("failed")); + }, [flow.isInWorldApp, isHostVerifying, flow.result, handleVerify]); + + // Auto-close on success: immediate in World App (no visible UI), delayed in bridge flow + useEffect(() => { + if (isSuccess) { + if (flow.isInWorldApp) { + onOpenChange(false); + } else if (autoClose) { + const timer = setTimeout(() => onOpenChange(false), 2500); + return () => clearTimeout(timer); + } + } + }, [isSuccess, autoClose, onOpenChange, flow.isInWorldApp]); + + // In World App context, the host app handles all UI — render nothing. + if (flow.isInWorldApp) { + return null; + } const stage = getVisualStage(isSuccess, isError, isHostVerifying); const showSimulatorCallout = config.environment === "staging"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b2d47ba..c2dafb16 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,6 +45,9 @@ importers: '@worldcoin/idkit': specifier: workspace:* version: link:../../packages/react + '@worldcoin/minikit-js': + specifier: ^1.11.0 + version: 1.11.0(@types/react@18.3.28)(react@18.3.1)(typescript@5.9.3)(viem@2.47.2(typescript@5.9.3)) eruda: specifier: ^3.4.1 version: 3.4.3 @@ -57,6 +60,9 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + viem: + specifier: ^2.47.2 + version: 2.47.2(typescript@5.9.3) devDependencies: '@types/react': specifier: ^18.3.12 @@ -169,6 +175,9 @@ packages: '@adobe/css-tools@4.4.4': resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + '@adraffy/ens-normalize@1.11.1': + resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} + '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -558,6 +567,18 @@ packages: cpu: [x64] os: [win32] + '@noble/ciphers@1.3.0': + resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.1': + resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} + engines: {node: ^14.21.3 || >=16} + + '@noble/curves@1.9.7': + resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + engines: {node: ^14.21.3 || >=16} + '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} @@ -690,6 +711,15 @@ packages: cpu: [x64] os: [win32] + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} + + '@scure/bip32@1.7.0': + resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} + + '@scure/bip39@1.6.0': + resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -816,6 +846,28 @@ packages: '@vitest/utils@4.0.18': resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@worldcoin/idkit-core@2.1.0': + resolution: {integrity: sha512-6MOFcOGqpk3iiW1i25NYmRJ6JUO8KetqS5Ic0l2cqnEEKvjMa/NT1+WTuenjTUF//Mqwy5kWgtVBzUqeyEfQ2g==} + engines: {node: '>=12.4'} + + '@worldcoin/minikit-js@1.11.0': + resolution: {integrity: sha512-EkkKa0Ei4tBDq75QpyLPEac4N5wpUGX/J990LeyKuKAJWsFqtZltIxHtWPyPY+a9WKbfboW29NL7ZnEl1xg47g==} + engines: {node: '>= 16'} + peerDependencies: + react: ^17 || ^18 || ^19 + viem: ^2.23.5 + + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -854,10 +906,19 @@ packages: ast-v8-to-istanbul@0.3.10: resolution: {integrity: sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + body-parser@2.2.2: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} + browser-or-node@3.0.0-pre.0: + resolution: {integrity: sha512-J6/RvtjajlHVJMBZ/Xk+5O9E8CKUHOZDz5e/eG8UBjWBSB/d/tgqFOua/xxqP+eGfgZlaJHELHnC3mdsVTTMIQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bundle-require@5.1.0: resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1043,6 +1104,9 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} @@ -1133,6 +1197,9 @@ packages: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -1151,6 +1218,11 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + isows@1.0.7: + resolution: {integrity: sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==} + peerDependencies: + ws: '*' + istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -1287,6 +1359,22 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + ox@0.1.8: + resolution: {integrity: sha512-GJl6uKXxhPq/XgyvAnIokGuGU/pt9CU8reRJjzi4a02HOpLc2CEXXD4bRCITFFAzdRqHj3DQ6GDS7PlCytPM/A==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + + ox@0.14.0: + resolution: {integrity: sha512-WLOB7IKnmI3Ol6RAqY7CJdZKl8QaI44LN91OGF1061YIeN6bL5IsFcdp7+oQShRyamE/8fW/CBRWhJAOzI35Dw==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -1623,10 +1711,23 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + viem@2.47.2: + resolution: {integrity: sha512-etDIwDgmDiGaPg8rUbJtUFuC3/nAJCbhMYyfh5dOcqNNkzBWTNcS2VluPSM5JVo+9U3b2hle2RkBEq3+xyvlvg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1724,6 +1825,18 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} @@ -1747,10 +1860,27 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + snapshots: '@adobe/css-tools@4.4.4': {} + '@adraffy/ens-normalize@1.11.1': {} + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -1994,6 +2124,16 @@ snapshots: '@next/swc-win32-x64-msvc@15.5.12': optional: true + '@noble/ciphers@1.3.0': {} + + '@noble/curves@1.9.1': + dependencies: + '@noble/hashes': 1.8.0 + + '@noble/curves@1.9.7': + dependencies: + '@noble/hashes': 1.8.0 + '@noble/hashes@1.8.0': {} '@noble/secp256k1@2.3.0': {} @@ -2073,6 +2213,19 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.57.0': optional: true + '@scure/base@1.2.6': {} + + '@scure/bip32@1.7.0': + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + + '@scure/bip39@1.6.0': + dependencies: + '@noble/hashes': 1.8.0 + '@scure/base': 1.2.6 + '@standard-schema/spec@1.1.0': {} '@swc/helpers@0.5.15': @@ -2231,6 +2384,35 @@ snapshots: '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 + '@worldcoin/idkit-core@2.1.0(@types/react@18.3.28)(react@18.3.1)(typescript@5.9.3)': + dependencies: + browser-or-node: 3.0.0-pre.0 + buffer: 6.0.3 + ox: 0.1.8(typescript@5.9.3) + zustand: 4.5.7(@types/react@18.3.28)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + - react + - typescript + - zod + + '@worldcoin/minikit-js@1.11.0(@types/react@18.3.28)(react@18.3.1)(typescript@5.9.3)(viem@2.47.2(typescript@5.9.3))': + dependencies: + '@worldcoin/idkit-core': 2.1.0(@types/react@18.3.28)(react@18.3.1)(typescript@5.9.3) + abitype: 1.2.3(typescript@5.9.3) + react: 18.3.1 + viem: 2.47.2(typescript@5.9.3) + transitivePeerDependencies: + - '@types/react' + - immer + - typescript + - zod + + abitype@1.2.3(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -2262,6 +2444,8 @@ snapshots: estree-walker: 3.0.3 js-tokens: 9.0.1 + base64-js@1.5.1: {} + body-parser@2.2.2: dependencies: bytes: 3.1.2 @@ -2276,6 +2460,13 @@ snapshots: transitivePeerDependencies: - supports-color + browser-or-node@3.0.0-pre.0: {} + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + bundle-require@5.1.0(esbuild@0.27.2): dependencies: esbuild: 0.27.2 @@ -2441,6 +2632,8 @@ snapshots: etag@1.8.1: {} + eventemitter3@5.0.1: {} + expect-type@1.3.0: {} express@5.2.1: @@ -2565,6 +2758,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + indent-string@4.0.0: {} inherits@2.0.4: {} @@ -2575,6 +2770,10 @@ snapshots: is-promise@4.0.0: {} + isows@1.0.7(ws@8.18.3): + dependencies: + ws: 8.18.3 + istanbul-lib-coverage@3.2.2: {} istanbul-lib-report@3.0.1: @@ -2694,6 +2893,35 @@ snapshots: dependencies: wrappy: 1.0.2 + ox@0.1.8(typescript@5.9.3): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + + ox@0.14.0(typescript@5.9.3): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - zod + p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -3075,8 +3303,29 @@ snapshots: unpipe@1.0.0: {} + use-sync-external-store@1.6.0(react@18.3.1): + dependencies: + react: 18.3.1 + vary@1.1.2: {} + viem@2.47.2(typescript@5.9.3): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.9.3) + isows: 1.0.7(ws@8.18.3) + ox: 0.14.0(typescript@5.9.3) + ws: 8.18.3 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + vite@7.3.1(@types/node@20.19.30)(tsx@4.21.0): dependencies: esbuild: 0.27.2 @@ -3151,6 +3400,8 @@ snapshots: wrappy@1.0.2: {} + ws@8.18.3: {} + y18n@4.0.3: {} y18n@5.0.8: {} @@ -3185,3 +3436,10 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 + + zustand@4.5.7(@types/react@18.3.28)(react@18.3.1): + dependencies: + use-sync-external-store: 1.6.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.28 + react: 18.3.1 From cb119ec134bb957bed469a934eac6b297014bb23 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:03:00 -0700 Subject: [PATCH 2/7] update --- js/examples/nextjs/app/ui.tsx | 5 +-- js/packages/react/src/types/common.ts | 2 +- .../react/src/widget/IDKitSessionWidget.tsx | 31 ++++++++++++++++--- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/js/examples/nextjs/app/ui.tsx b/js/examples/nextjs/app/ui.tsx index 6708e759..193da6fb 100644 --- a/js/examples/nextjs/app/ui.tsx +++ b/js/examples/nextjs/app/ui.tsx @@ -284,11 +284,8 @@ export function DemoClient(): ReactElement { rp_context={widgetRpContext} allow_legacy_proofs={true} preset={widgetPreset} - onSuccess={() => { - console.log("success"); - }} + onSuccess={() => {}} handleVerify={async (result) => { - console.log("test"); const verified = await verifyProof(result); setWidgetVerifyResult(verified); }} diff --git a/js/packages/react/src/types/common.ts b/js/packages/react/src/types/common.ts index a05a2530..b328efd1 100644 --- a/js/packages/react/src/types/common.ts +++ b/js/packages/react/src/types/common.ts @@ -20,6 +20,6 @@ export type IDKitHookResult = { result: TResult | null; errorCode: IDKitErrorCodes | null; isOpen: boolean; - /** `true` when running inside World App (mini app context). The widget renders nothing and `handleVerify` is skipped. */ + /** Use `isInWorldApp` to determine if the widget is running inside the World App (mini app context). */ isInWorldApp: boolean; }; diff --git a/js/packages/react/src/widget/IDKitSessionWidget.tsx b/js/packages/react/src/widget/IDKitSessionWidget.tsx index 867dd6b4..03e9c497 100644 --- a/js/packages/react/src/widget/IDKitSessionWidget.tsx +++ b/js/packages/react/src/widget/IDKitSessionWidget.tsx @@ -102,13 +102,34 @@ export function IDKitSessionWidget({ }); }, [effectiveErrorCode, onError]); - // Auto-close on success + // In World App context there's no UI to render HostAppVerificationState, + // so invoke handleVerify programmatically when the proof arrives. useEffect(() => { - if (isSuccess && autoClose) { - const timer = setTimeout(() => onOpenChange(false), 2500); - return () => clearTimeout(timer); + if (!flow.isInWorldApp || !isHostVerifying || !flow.result || !handleVerify) { + return; } - }, [isSuccess, autoClose, onOpenChange]); + + void Promise.resolve(handleVerify(flow.result)) + .then(() => setHostVerifyResult("passed")) + .catch(() => setHostVerifyResult("failed")); + }, [flow.isInWorldApp, isHostVerifying, flow.result, handleVerify]); + + // Auto-close on success: immediate in World App (no visible UI), delayed in bridge flow + useEffect(() => { + if (isSuccess) { + if (flow.isInWorldApp) { + onOpenChange(false); + } else if (autoClose) { + const timer = setTimeout(() => onOpenChange(false), 2500); + return () => clearTimeout(timer); + } + } + }, [isSuccess, autoClose, onOpenChange, flow.isInWorldApp]); + + // In World App context, the host app handles all UI — render nothing. + if (flow.isInWorldApp) { + return null; + } const stage = getVisualStage(isSuccess, isError, isHostVerifying); const showSimulatorCallout = config.environment === "staging"; From 50cc4b7b9716eee8a800d35754d2fe5ee445aab3 Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:03:49 -0700 Subject: [PATCH 3/7] fmt --- js/packages/react/src/widget/IDKitRequestWidget.tsx | 7 ++++++- js/packages/react/src/widget/IDKitSessionWidget.tsx | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/js/packages/react/src/widget/IDKitRequestWidget.tsx b/js/packages/react/src/widget/IDKitRequestWidget.tsx index d5b647ba..d2fe55fa 100644 --- a/js/packages/react/src/widget/IDKitRequestWidget.tsx +++ b/js/packages/react/src/widget/IDKitRequestWidget.tsx @@ -102,7 +102,12 @@ export function IDKitRequestWidget({ // In World App context there's no UI to render HostAppVerificationState, // so invoke handleVerify programmatically when the proof arrives. useEffect(() => { - if (!flow.isInWorldApp || !isHostVerifying || !flow.result || !handleVerify) { + if ( + !flow.isInWorldApp || + !isHostVerifying || + !flow.result || + !handleVerify + ) { return; } diff --git a/js/packages/react/src/widget/IDKitSessionWidget.tsx b/js/packages/react/src/widget/IDKitSessionWidget.tsx index 03e9c497..6610fc12 100644 --- a/js/packages/react/src/widget/IDKitSessionWidget.tsx +++ b/js/packages/react/src/widget/IDKitSessionWidget.tsx @@ -105,7 +105,12 @@ export function IDKitSessionWidget({ // In World App context there's no UI to render HostAppVerificationState, // so invoke handleVerify programmatically when the proof arrives. useEffect(() => { - if (!flow.isInWorldApp || !isHostVerifying || !flow.result || !handleVerify) { + if ( + !flow.isInWorldApp || + !isHostVerifying || + !flow.result || + !handleVerify + ) { return; } From ae72ab6b0a09e5b51116196ed73a171168ec2ada Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:05:22 -0700 Subject: [PATCH 4/7] pr feedback --- .../react/src/widget/IDKitRequestWidget.tsx | 17 ++++++++--------- .../react/src/widget/IDKitSessionWidget.tsx | 17 ++++++++--------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/js/packages/react/src/widget/IDKitRequestWidget.tsx b/js/packages/react/src/widget/IDKitRequestWidget.tsx index d2fe55fa..fb1a26a1 100644 --- a/js/packages/react/src/widget/IDKitRequestWidget.tsx +++ b/js/packages/react/src/widget/IDKitRequestWidget.tsx @@ -116,17 +116,16 @@ export function IDKitRequestWidget({ .catch(() => setHostVerifyResult("failed")); }, [flow.isInWorldApp, isHostVerifying, flow.result, handleVerify]); - // Auto-close on success: immediate in World App (no visible UI), delayed in bridge flow + // In World App there's no visible UI, so auto-close immediately on success or error. + // In bridge flow, only auto-close on success after the 2.5s delay (errors show retry UI). useEffect(() => { - if (isSuccess) { - if (flow.isInWorldApp) { - onOpenChange(false); - } else if (autoClose) { - const timer = setTimeout(() => onOpenChange(false), 2500); - return () => clearTimeout(timer); - } + if (flow.isInWorldApp && (isSuccess || isError)) { + onOpenChange(false); + } else if (isSuccess && autoClose) { + const timer = setTimeout(() => onOpenChange(false), 2500); + return () => clearTimeout(timer); } - }, [isSuccess, autoClose, onOpenChange, flow.isInWorldApp]); + }, [isSuccess, isError, autoClose, onOpenChange, flow.isInWorldApp]); // In World App context, the host app handles all UI — render nothing. if (flow.isInWorldApp) { diff --git a/js/packages/react/src/widget/IDKitSessionWidget.tsx b/js/packages/react/src/widget/IDKitSessionWidget.tsx index 6610fc12..b1f81ff1 100644 --- a/js/packages/react/src/widget/IDKitSessionWidget.tsx +++ b/js/packages/react/src/widget/IDKitSessionWidget.tsx @@ -119,17 +119,16 @@ export function IDKitSessionWidget({ .catch(() => setHostVerifyResult("failed")); }, [flow.isInWorldApp, isHostVerifying, flow.result, handleVerify]); - // Auto-close on success: immediate in World App (no visible UI), delayed in bridge flow + // In World App there's no visible UI, so auto-close immediately on success or error. + // In bridge flow, only auto-close on success after the 2.5s delay (errors show retry UI). useEffect(() => { - if (isSuccess) { - if (flow.isInWorldApp) { - onOpenChange(false); - } else if (autoClose) { - const timer = setTimeout(() => onOpenChange(false), 2500); - return () => clearTimeout(timer); - } + if (flow.isInWorldApp && (isSuccess || isError)) { + onOpenChange(false); + } else if (isSuccess && autoClose) { + const timer = setTimeout(() => onOpenChange(false), 2500); + return () => clearTimeout(timer); } - }, [isSuccess, autoClose, onOpenChange, flow.isInWorldApp]); + }, [isSuccess, isError, autoClose, onOpenChange, flow.isInWorldApp]); // In World App context, the host app handles all UI — render nothing. if (flow.isInWorldApp) { From 6b8f2f7b8d3334c066c327bac8d4f265ca5cc04d Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:35:54 -0700 Subject: [PATCH 5/7] add use effect cleanup --- js/packages/react/src/widget/IDKitRequestWidget.tsx | 12 ++++++++++-- js/packages/react/src/widget/IDKitSessionWidget.tsx | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/js/packages/react/src/widget/IDKitRequestWidget.tsx b/js/packages/react/src/widget/IDKitRequestWidget.tsx index fb1a26a1..d14b57e3 100644 --- a/js/packages/react/src/widget/IDKitRequestWidget.tsx +++ b/js/packages/react/src/widget/IDKitRequestWidget.tsx @@ -111,9 +111,17 @@ export function IDKitRequestWidget({ return; } + let cancelled = false; void Promise.resolve(handleVerify(flow.result)) - .then(() => setHostVerifyResult("passed")) - .catch(() => setHostVerifyResult("failed")); + .then(() => { + if (!cancelled) setHostVerifyResult("passed"); + }) + .catch(() => { + if (!cancelled) setHostVerifyResult("failed"); + }); + return () => { + cancelled = true; + }; }, [flow.isInWorldApp, isHostVerifying, flow.result, handleVerify]); // In World App there's no visible UI, so auto-close immediately on success or error. diff --git a/js/packages/react/src/widget/IDKitSessionWidget.tsx b/js/packages/react/src/widget/IDKitSessionWidget.tsx index b1f81ff1..a82c5eac 100644 --- a/js/packages/react/src/widget/IDKitSessionWidget.tsx +++ b/js/packages/react/src/widget/IDKitSessionWidget.tsx @@ -114,9 +114,17 @@ export function IDKitSessionWidget({ return; } + let cancelled = false; void Promise.resolve(handleVerify(flow.result)) - .then(() => setHostVerifyResult("passed")) - .catch(() => setHostVerifyResult("failed")); + .then(() => { + if (!cancelled) setHostVerifyResult("passed"); + }) + .catch(() => { + if (!cancelled) setHostVerifyResult("failed"); + }); + return () => { + cancelled = true; + }; }, [flow.isInWorldApp, isHostVerifying, flow.result, handleVerify]); // In World App there's no visible UI, so auto-close immediately on success or error. From 0625da134c25f379e4b6b960dfae2e3c076d7efe Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:30:41 -0700 Subject: [PATCH 6/7] simplify --- js/packages/react/src/hooks/common.ts | 2 -- js/packages/react/src/hooks/useIDKitFlow.ts | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/js/packages/react/src/hooks/common.ts b/js/packages/react/src/hooks/common.ts index 1c4ddb7d..4fff5b14 100644 --- a/js/packages/react/src/hooks/common.ts +++ b/js/packages/react/src/hooks/common.ts @@ -13,7 +13,6 @@ export type HookState = { connectorURI: string | null; result: TResult | null; errorCode: IDKitErrorCodes | null; - isInWorldApp: boolean; }; export function createInitialHookState(): HookState { @@ -23,7 +22,6 @@ export function createInitialHookState(): HookState { connectorURI: null, result: null, errorCode: null, - isInWorldApp: false, }; } diff --git a/js/packages/react/src/hooks/useIDKitFlow.ts b/js/packages/react/src/hooks/useIDKitFlow.ts index a92a664c..a87db18a 100644 --- a/js/packages/react/src/hooks/useIDKitFlow.ts +++ b/js/packages/react/src/hooks/useIDKitFlow.ts @@ -55,10 +55,9 @@ export function useIDKitFlow( connectorURI: null, result: null, errorCode: null, - isInWorldApp: isInWorldApp, }; }); - }, [isInWorldApp]); + }, []); useEffect(() => { if (!state.isOpen) { From c7595ed4ac20faf82e30494b0b2d730e4807e03a Mon Sep 17 00:00:00 2001 From: Takis Kakalis <80459599+Takaros999@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:38:23 -0700 Subject: [PATCH 7/7] update --- js/packages/react/src/hooks/useIDKitFlow.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/packages/react/src/hooks/useIDKitFlow.ts b/js/packages/react/src/hooks/useIDKitFlow.ts index a87db18a..12a907e0 100644 --- a/js/packages/react/src/hooks/useIDKitFlow.ts +++ b/js/packages/react/src/hooks/useIDKitFlow.ts @@ -164,7 +164,7 @@ export function useIDKitFlow( abortRef.current = null; } }; - }, [state.isOpen, runId]); + }, [state.isOpen, runId, isInWorldApp]); return { open, @@ -177,6 +177,6 @@ export function useIDKitFlow( result: state.result, errorCode: state.errorCode, isOpen: state.isOpen, - isInWorldApp: isInWorldApp, + isInWorldApp, }; }