diff --git a/package.json b/package.json index 979d02b..13a80e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@okta/okta-client-js", - "version": "0.6.0", + "version": "0.7.0", "private": true, "packageManager": "yarn@1.22.19", "engines": { diff --git a/packages/auth-foundation/package.json b/packages/auth-foundation/package.json index 2b160f8..b36c0c0 100644 --- a/packages/auth-foundation/package.json +++ b/packages/auth-foundation/package.json @@ -1,6 +1,6 @@ { "name": "@okta/auth-foundation", - "version": "0.6.0", + "version": "0.7.0", "type": "module", "main": "dist/esm/index.js", "module": "dist/esm/index.js", diff --git a/packages/auth-foundation/src/utils/TaskBridge.ts b/packages/auth-foundation/src/utils/TaskBridge.ts index 7954101..bb61ab2 100644 --- a/packages/auth-foundation/src/utils/TaskBridge.ts +++ b/packages/auth-foundation/src/utils/TaskBridge.ts @@ -5,6 +5,14 @@ import { AuthSdkError } from '../errors/AuthSdkError.ts'; /** @useDeclaredType */ type TypeMap = Record; +/** @internal */ +function warn (message: string) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + if (TaskBridge.warnOnBusMessageMismatch) { + console.warn(message); + } +} + /** * A bridge for passing messages between a `TaskHandler` and a `Requestor`. The `Requestor` is "asking" the `TaskHandler` * To perform a `Task` on it's behave. Loosely based on TCP @@ -111,6 +119,11 @@ export abstract class TaskBridge { // This channel is meant for the Receiver to send the results (aka `HandlerMessage` messages) // ignore all Requestor events received (aka `RequestorMessage`) responseChannel.onmessage = (event) => { + const { __v } = event.data; + if (__v !== TaskBridge.BridgeVersion) { + warn(`[Requestor] received mismatched message (requestor=${TaskBridge.BridgeVersion}, message=${__v})`); + } + if (request.signal.aborted || 'action' in event.data) { return; // ignore message } @@ -162,6 +175,10 @@ export abstract class TaskBridge { this.#channel.onmessage = async (evt, reply) => { const { requestId, __v, ...rest } = evt.data; + if (__v !== TaskBridge.BridgeVersion) { + warn(`[Subscriber] received mismatched message (subscriber=${TaskBridge.BridgeVersion}, message=${__v})`); + } + if (!requestId) { return; } @@ -260,6 +277,12 @@ export namespace TaskBridge { // NOTE: update this value when the payload structure of the TaskBridge changes export const BridgeVersion = 2; + /** + * Toggles whether `console.warn` messages are broadcasted when bus messages are received with + * a mismatched BridgeVersion. Useful for debugging communication issues. + */ + export let warnOnBusMessageMismatch: boolean = false; + /** * Possible `status` values indicating the process of an orchestrated request */ diff --git a/packages/auth-foundation/test/spec/utils/TaskBridge.spec.ts b/packages/auth-foundation/test/spec/utils/TaskBridge.spec.ts index b24c987..9be0475 100644 --- a/packages/auth-foundation/test/spec/utils/TaskBridge.spec.ts +++ b/packages/auth-foundation/test/spec/utils/TaskBridge.spec.ts @@ -363,4 +363,61 @@ describe('TaskBridge', () => { jest.useRealTimers(); }); + describe('features', () => { + describe('Mismatch message version warning', () => { + let channel: BroadcastChannel; + + beforeEach(() => { + TaskBridge.warnOnBusMessageMismatch = true; + channel = new BroadcastChannel('test'); + jest.spyOn(console, 'warn').mockReturnValue(); + }); + + afterEach(() => { + channel.close(); + TaskBridge.warnOnBusMessageMismatch = false; + }); + + test('Requestor will warn get reply doesn\'t match version', async () => { + channel.onmessage = (event) => { + const responseChannel = new BroadcastChannel(event.data.requestId); + responseChannel.postMessage({ status: 'SUCCESS', data: { foo: '1' }, __v: 'foo' }); + responseChannel.close(); + }; + + const { result } = sender.send({ foo: 1, bar: 2 }); + await result; + + expect(console.warn).toHaveBeenCalled(); + }); + + test('Subscriber will warn get reply doesn\'t match version', async () => { + receiver.subscribe(async (message, reply) => { + reply({ foo: '2', bar: '1' }); + }); + + channel.postMessage({ requestId: 'foo', __v: 'foo', data: { foo: 1 }}); + + // this message is only used in the test to give time for the first message to send + const { result } = sender.send({ foo: 1, bar: 2 }); + await result; + + expect(console.warn).toHaveBeenCalled(); + }); + + test('No warnings when disabled', async () => { + TaskBridge.warnOnBusMessageMismatch = false; + + receiver.subscribe(async (message, reply) => { + reply({ foo: '2', bar: '1' }); + }); + + const { result } = sender.send({ foo: 1, bar: 2 }); + await result; + + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + }); + }); diff --git a/packages/oauth2-flows/package.json b/packages/oauth2-flows/package.json index a0011cc..1aeaeec 100644 --- a/packages/oauth2-flows/package.json +++ b/packages/oauth2-flows/package.json @@ -1,6 +1,6 @@ { "name": "@okta/oauth2-flows", - "version": "0.6.0", + "version": "0.7.0", "type": "module", "main": "dist/esm/index.js", "module": "dist/esm/index.js", diff --git a/packages/spa-platform/package.json b/packages/spa-platform/package.json index a554386..95c842a 100644 --- a/packages/spa-platform/package.json +++ b/packages/spa-platform/package.json @@ -1,6 +1,6 @@ { "name": "@okta/spa-platform", - "version": "0.6.0", + "version": "0.7.0", "type": "module", "main": "dist/esm/index.js", "module": "dist/esm/index.js", diff --git a/packages/spa-platform/test/spec/orchestrators/HostOrchestrator.spec.ts b/packages/spa-platform/test/spec/orchestrators/HostOrchestrator.spec.ts index ed3c2ea..bac70e2 100644 --- a/packages/spa-platform/test/spec/orchestrators/HostOrchestrator.spec.ts +++ b/packages/spa-platform/test/spec/orchestrators/HostOrchestrator.spec.ts @@ -28,6 +28,8 @@ class MockOrchestrator extends TokenOrchestrator { describe('HostOrchestrator', () => { + jest.retryTimes(3); + const authParams = { issuer: 'http://fake.okta.com', clientId: 'fakeClientId',