diff --git a/README.md b/README.md index f6dc7451..229a404e 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,8 @@ Supply these to the `init` call (2nd argument) { debug?: false, // enable debug mode to log all info and errors persistUser?: true | false // default value depends on environment, see below under "persisting users" - host?: "https://tracking.bucket.co", // don't change this + host?: "https://tracking.bucket.co", + sseHost?: "https://livemessaging.bucket.co" } ``` diff --git a/src/config.ts b/src/config.ts index 7bed0c8d..95d298ac 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,2 @@ export const TRACKING_HOST = "https://tracking.bucket.co"; -export const ABLY_REST_HOST = "https://livemessaging.bucket.co"; -export const ABLY_REALTIME_HOST = ABLY_REST_HOST; +export const SSE_REALTIME_HOST = "https://livemessaging.bucket.co"; diff --git a/src/main.ts b/src/main.ts index e72c8265..37d347de 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ import { isForNode } from "is-bundling-for-browser-or-node"; import { version } from "../package.json"; import type { FeedbackPosition, FeedbackTranslations } from "./feedback/types"; -import { TRACKING_HOST } from "./config"; +import { SSE_REALTIME_HOST, TRACKING_HOST } from "./config"; import { defaultFeedbackPromptHandler } from "./default-feedback-prompt-handler"; import * as feedbackLib from "./feedback"; import { @@ -45,6 +45,7 @@ export default function main() { let debug = false; let trackingKey: string | undefined = undefined; let trackingHost: string = TRACKING_HOST; + let sseHost: string = SSE_REALTIME_HOST; let sessionUserId: string | undefined = undefined; let persistUser: boolean = !isForNode; let liveSatisfactionActive: boolean = false; @@ -112,6 +113,7 @@ export default function main() { if (options.debug) debug = options.debug; if (options.host) trackingHost = options.host; + if (options.sseHost) sseHost = options.sseHost; if (options.feedback?.ui?.position) { feedbackPosition = options.feedback?.ui?.position; @@ -295,7 +297,7 @@ export default function main() { userId, body.channel, (message) => handleFeedbackPromptRequest(userId!, message), - { debug }, + { debug, sseHost }, ); feedbackPromptingUserId = userId; diff --git a/src/sse.ts b/src/sse.ts index b1258e57..a6dae33e 100644 --- a/src/sse.ts +++ b/src/sse.ts @@ -1,6 +1,6 @@ import fetch from "cross-fetch"; -import { ABLY_REALTIME_HOST, ABLY_REST_HOST } from "./config"; +import { SSE_REALTIME_HOST } from "./config"; interface AblyTokenDetails { token: string; @@ -23,6 +23,7 @@ export class AblySSEChannel { private userId: string, private channel: string, private ablyAuthUrl: string, + private sseHost: string, private messageHandler: (message: any) => void, options?: { debug?: boolean; @@ -75,9 +76,8 @@ export class AblySSEChannel { private async refreshToken() { const tokenRequest = await this.refreshTokenRequest(); - const res = await fetch( - `${ABLY_REST_HOST}/keys/${encodeURIComponent( + `${this.sseHost}/keys/${encodeURIComponent( tokenRequest.keyName, )}/requestToken`, { @@ -176,7 +176,7 @@ export class AblySSEChannel { const token = await this.refreshToken(); this.eventSource = new EventSource( - `${ABLY_REALTIME_HOST}/sse?v=1.2&accessToken=${encodeURIComponent( + `${this.sseHost}/sse?v=1.2&accessToken=${encodeURIComponent( token.token, )}&channels=${encodeURIComponent(this.channel)}&rewind=1`, ); @@ -267,11 +267,21 @@ export function openAblySSEChannel( userId: string, channel: string, callback: (req: object) => void, - options?: { debug?: boolean; retryInterval?: number; retryCount?: number }, + options?: { + debug?: boolean; + retryInterval?: number; + retryCount?: number; + sseHost?: string; + }, ) { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, callback, { - debug: options?.debug, - }); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + options?.sseHost || SSE_REALTIME_HOST, + callback, + { debug: options?.debug }, + ); sse.open(); diff --git a/src/types.ts b/src/types.ts index a45c44cb..630856a2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,6 +10,7 @@ export type Key = string; export type Options = { persistUser?: boolean; host?: string; + sseHost?: string; debug?: boolean; feedback?: { /** diff --git a/test/sse.test.ts b/test/sse.test.ts index 676bd9ef..b6c5edfc 100644 --- a/test/sse.test.ts +++ b/test/sse.test.ts @@ -2,7 +2,6 @@ import flushPromises from "flush-promises"; import nock from "nock"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; -import { ABLY_REST_HOST } from "../src/config"; import { AblySSEChannel, closeAblySSEChannel, @@ -10,6 +9,7 @@ import { } from "../src/sse"; const ablyAuthUrl = "https://example.com/123/feedback/prompting-auth"; +const sseHost = "https://ssehost.com"; const tokenRequest = { keyName: "key-name", other: "other", @@ -38,7 +38,7 @@ function setupAuthNock(success: boolean | number) { } function setupTokenNock(success: boolean) { - const n = nock(`${ABLY_REST_HOST}/keys/${tokenRequest.keyName}`).post( + const n = nock(`${sseHost}/keys/${tokenRequest.keyName}`).post( /.*\/requestToken/, { ...tokenRequest, @@ -61,7 +61,13 @@ describe("connection handling", () => { }); test("rejects if auth endpoint is not success", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); setupAuthNock(false); @@ -71,7 +77,13 @@ describe("connection handling", () => { }); test("rejects if auth endpoint is not 200", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); setupAuthNock(403); @@ -81,7 +93,13 @@ describe("connection handling", () => { }); test("rejects if token endpoint rejects", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); setupAuthNock(true); setupTokenNock(false); @@ -92,7 +110,13 @@ describe("connection handling", () => { }); test("obtains token, connects and subscribes, then closes", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); const addEventListener = vi.fn(); const close = vi.fn(); @@ -128,7 +152,13 @@ describe("connection handling", () => { }); test("does not try to re-connect if already connecting", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); const close = vi.fn(); vi.mocked(window.EventSource).mockReturnValue({ @@ -150,7 +180,13 @@ describe("connection handling", () => { }); test("does not re-connect if already connected", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); const close = vi.fn(); vi.mocked(window.EventSource).mockReturnValue({ @@ -169,7 +205,13 @@ describe("connection handling", () => { }); test("disconnects only if connected", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); const close = vi.fn(); vi.mocked(window.EventSource).mockReturnValue({ @@ -197,7 +239,13 @@ describe("message handling", () => { test("passes message to callback", async () => { const callback = vi.fn(); - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, callback); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + callback, + ); let messageCallback: ((e: Event) => void) | undefined = undefined; const addEventListener = (event: string, cb: (e: Event) => void) => { @@ -231,7 +279,13 @@ describe("message handling", () => { }); test("disconnects on unknown event source errors without data", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); let errorCallback: ((e: Event) => Promise) | undefined = undefined; const addEventListener = (event: string, cb: (e: Event) => void) => { @@ -255,8 +309,14 @@ describe("message handling", () => { }); test("disconnects on unknown event source errors with data", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); - + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); + sseHost; let errorCallback: ((e: Event) => Promise) | undefined = undefined; const addEventListener = (event: string, cb: (e: Event) => void) => { if (event === "error") { @@ -284,7 +344,13 @@ describe("message handling", () => { }); test("disconnects when ably reports token is expired", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); let errorCallback: ((e: Event) => Promise) | undefined = undefined; const addEventListener = (event: string, cb: (e: Event) => void) => { @@ -330,7 +396,13 @@ describe("automatic retries", () => { }); test("opens and connects to a channel", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); const n1 = setupAuthNock(true); const n2 = setupTokenNock(true); @@ -346,7 +418,13 @@ describe("automatic retries", () => { }); test("opens and connects later to a failed channel", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); const n1 = setupAuthNock(false); @@ -374,7 +452,13 @@ describe("automatic retries", () => { }); test("only, ever, allow one connection to SSE", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); const n1 = setupAuthNock(true); const n2 = setupTokenNock(true); @@ -401,7 +485,13 @@ describe("automatic retries", () => { }); test("resets retry count on successfull connect", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); // mock event source let errorCallback: ((e: Event) => Promise) | undefined = undefined; @@ -457,7 +547,13 @@ describe("automatic retries", () => { }); test("reconnects if manually disconnected", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); vi.mocked(window.EventSource).mockReturnValue({ addEventListener: vi.fn(), @@ -494,7 +590,13 @@ describe("automatic retries", () => { }); test("opens and does not connect later to a failed channel if no retries", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); const n1 = setupAuthNock(false); @@ -516,7 +618,13 @@ describe("automatic retries", () => { }); test("closes an open channel", async () => { - const sse = new AblySSEChannel(userId, channel, ablyAuthUrl, vi.fn()); + const sse = new AblySSEChannel( + userId, + channel, + ablyAuthUrl, + sseHost, + vi.fn(), + ); const n1 = setupAuthNock(true); const n2 = setupTokenNock(true); @@ -563,7 +671,9 @@ describe("helper open and close functions", () => { const n1 = setupAuthNock(true); const n2 = setupTokenNock(true); - const sse = openAblySSEChannel(ablyAuthUrl, userId, channel, vi.fn()); + const sse = openAblySSEChannel(ablyAuthUrl, userId, channel, vi.fn(), { + sseHost, + }); expect(sse.isActive()).toBe(true);