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,
};
}