Skip to content
5 changes: 5 additions & 0 deletions core/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ export const createCapacitor = (win: WindowCapacitor): CapacitorInstance => {
return pluginHeader ? addListenerNative : addListener;
case 'removeListener':
return removeListener;
// Promise-machinery short-circuit. See #8472 for failure modes.
case 'then':
case 'catch':
case 'finally':
Comment on lines +177 to +179

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When a plugin defines a method named catch or finally, the proxy now returns undefined instead of the method wrapper, so the method is inaccessible for both JS and native implementations. Promise assimilation only reads then; keep only the then case so arbitrary plugin method names remain supported.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At core/src/runtime.ts, line 177:

<comment>When a plugin defines a method named `catch` or `finally`, the proxy now returns `undefined` instead of the method wrapper, so the method is inaccessible for both JS and native implementations. Promise assimilation only reads `then`; keep only the `then` case so arbitrary plugin method names remain supported.</comment>

<file context>
@@ -173,6 +173,11 @@ export const createCapacitor = (win: WindowCapacitor): CapacitorInstance => {
             case 'removeListener':
               return removeListener;
+            // Promise-machinery short-circuit. See #8472 for failure modes.
+            case 'then':
+            case 'catch':
+            case 'finally':
</file context>
Suggested change
case 'then':
case 'catch':
case 'finally':
case 'then':

return undefined;
default:
return createPluginMethodWrapper(prop);
}
Expand Down
17 changes: 17 additions & 0 deletions core/src/tests/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,23 @@ describe('plugin', () => {
],
});
};

it('plugin proxy must not be a Thenable (Promise.resolve chaining short-circuit)', async () => {
// Web platform; no native bridge headers. registerPlugin returns a Proxy
// that wraps method dispatch. The proxy MUST NOT be unintentionally Thenable
// — otherwise Promise.resolve(proxy) and `await proxy` invoke proxy.then(...)
// and dispatch a bogus "then" method to the native bridge (or web stub).
// See GH issue #8472 for the production failure modes that motivated this regression test.
cap = initCapacitorGlobal(win);
const Awesome = cap.registerPlugin('Awesome');

expect(typeof (Awesome as unknown as { then?: unknown }).then).toBe('undefined');
expect(typeof (Awesome as unknown as { catch?: unknown }).catch).toBe('undefined');
expect(typeof (Awesome as unknown as { finally?: unknown }).finally).toBe('undefined');

const resolved = await Promise.resolve(Awesome);
expect(resolved).toBe(Awesome);
});
});

interface AwesomePlugin extends Plugin {
Expand Down
Loading