diff --git a/.github/workflows/publish-sdk-angular.yml b/.github/workflows/publish-sdk-angular.yml index b002bfd..a984a6b 100644 --- a/.github/workflows/publish-sdk-angular.yml +++ b/.github/workflows/publish-sdk-angular.yml @@ -17,6 +17,9 @@ jobs: cache: 'npm' - name: Angular SDK - Install packages run: yarn install --frozen-lockfile + - name: Copy Core to Angular + working-directory: ./packages/sdk-angular + run: yarn get-sdk-core - name: Angular SDK - Test run: yarn test:sdk-angular @@ -35,6 +38,6 @@ jobs: run: yarn build:sdk-angular - name: Angular SDK - Publish working-directory: ./packages/sdk-angular/dist/fusionauth-angular-sdk - run: npm publish + run: npm publish --dry-run env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN_ANGULAR_SDK }} diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index c28b69e..e5d9f6f 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -19,6 +19,9 @@ jobs: cache: 'npm' - name: Install dependencies run: yarn install --frozen-lockfile + - name: Copy Core to Angular + working-directory: ./packages/sdk-angular + run: yarn get-sdk-core - name: Build Core run: yarn build:core - name: Run Tests diff --git a/.gitignore b/.gitignore index d048071..7afdcc2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules dist +packages/sdk-angular/projects/fusionauth-angular-sdk/src/sdkcore yarn-error.log diff --git a/README.md b/README.md index 799a70e..8a4e81a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +# Table of Contents + +- [Development](#development) + - [Manual testing](#manual-testing) + - [Gotchas](#gotchas) +- [Architecture](#architecture) +- [Upgrade Policy](#upgrade-policy) + # FusionAuth Web SDKs This is the FusionAuth Web SDKs monorepo. This repo manages FusionAuth [React](https://fusionauth.io/docs/sdks/react-sdk), [Angular](https://fusionauth.io/docs/sdks/angular-sdk), and [Vue](https://fusionauth.io/docs/sdks/vue-sdk) SDKs. @@ -16,16 +24,39 @@ Install dependencies with `yarn install`. The SDKs share a core package that contains framework agnostic functionality. This package should be built before the the SDK is built--a step included in the build script for each SDK (for example `build:sdk-react`). -### Manual testing - -[yalc](https://github.com/wclr/yalc) is a way to test your changes locally. +## Manual testing You may use the FusionAuth Quickstarts to consume the package and test changes. See [React Quickstart](https://fusionauth.io/docs/quickstarts/quickstart-javascript-react-web), [Angular Quickstart](https://fusionauth.io/docs/quickstarts/quickstart-javascript-angular-web), & [Vue Quickstart](https://fusionauth.io/docs/quickstarts/quickstart-javascript-vue-web) -Be aware of what node version you are using to publish and consume the package with yalc--mismatched versions can make the link not work. +There is more than one way to test your changes locally, but [yalc](https://github.com/wclr/yalc) has serves devs of this project well, as long as you pay attention to the [gotchas](#gotchas). + +You may use the `yalc-pub:sdk` scripts in the root package.json. For example, `yarn yalc-pub:sdk-react`. These build the SDKs and publish to your yalc store so they may be consumed locally. + +Here's a potential iteration: +- Make your changes in one of the SDKs. (Consider adding something like `console.log('Hello 👋')` where you know you’ll see it, to confirm your changes have been consumed.) +- Build the SDK and publish a local build of the package. You may use: `yarn yalc-pub:sdk-angular` +- cd into the app that will consume the local package. +- `yalc add @fusionauth/angular-sdk` +- Start the app and observe your changes. + +### Gotchas + +If your changes are not being consumed as you expected, consider the following: + +- Delete `node_modules` and removing all previous yalc installations (`rm -rf node_modules && yalc remove --all`). Then reinstall dependencies and re-install from yalc. We've found this to provide consistent iterations, whereas `yalc push` has proved flakey for developers of this project. +- If your consuming application uses an application framework like Angular, Nuxt, or Next, consider deleting any cache directories (such as `.angular` or `.nuxt`) between iterations. +- Be aware of what node version you are using to publish and consume the package--mismatched node versions can cause yalc to flake with indication. If you decide to use something like `yarn link` instead of `yalc`, be aware of how your dependencies are being consumed via the symlink. `yalc` copies your assets directly, so it's a more realistic representation of the production build than a symlink. +## Architecture + +We use a monorepo because our SDKs share core functionality, which is contained in the @fusionauth-sdk/core package. This private module is bundled into the distributed SDK packages, allowing us to maintain core logic in a single place. + +For React and Vue, our build tool is [`Vite`](https://vitejs.dev/guide/). + +Angular differs slightly because [`@angular-devkit/build-angular:ng-packagr`](https://github.com/ng-packagr/ng-packagr), the builder for Angular libraries, doesn't support bundling dependencies from outside the Angular workspace. Our solution is to copy the core package's `src` directory into the Angular workspace without transpiling it, letting Angular's library builder handle it as if it were not an external dependency. This copied directory functions like a `dist`: it is git-ignored, and changes should be made in `packages/core`. The build process will then consume the updates. See [GitHub issue #84](https://github.com/FusionAuth/fusionauth-javascript-sdk/issues/84) for more details. + ## Upgrade Policy This library may periodically receive updates with bug fixes, security patches, tests, code samples, or documentation changes. diff --git a/package.json b/package.json index 64a1dbb..8249989 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,12 @@ "prepare": "husky", "build:core": "yarn workspace @fusionauth-sdk/core build", "build:lexicon": "yarn workspace @fusionauth-sdk/lexicon build", - "build:sdk-angular": "yarn build:lexicon && yarn build:core && yarn workspace sdk-angular-workspace build", + "build:sdk-angular": "yarn workspace sdk-angular-workspace build", "build:sdk-react": "yarn build:lexicon && yarn build:core && yarn workspace @fusionauth/react-sdk build", "build:sdk-vue": "yarn build:lexicon && yarn build:core && yarn workspace @fusionauth/vue-sdk build", "yalc-pub:sdk-react": "yarn build:sdk-react && yalc publish packages/sdk-react", "yalc-pub:sdk-vue": "yarn build:sdk-vue && yalc publish packages/sdk-vue", + "yalc-pub:sdk-angular": "yarn build:sdk-angular && yalc publish packages/sdk-angular/dist/fusionauth-angular-sdk", "test": "yarn test:lexicon && yarn test:core && yarn test:sdk-react && yarn test:sdk-angular && yarn test:sdk-vue", "test:core": "yarn workspace @fusionauth-sdk/core test", "test:lexicon": "yarn workspace @fusionauth-sdk/lexicon test", diff --git a/packages/core/package.json b/packages/core/package.json index 6dd062f..cdfe120 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -11,7 +11,7 @@ "dist" ], "scripts": { - "build": "tsc && vite build", + "build": "vite build", "dev:watch": "vite", "test:watch": "vitest", "test": "vitest --watch=false" diff --git a/packages/core/src/SDKCore/SDKCore.test.ts b/packages/core/src/SDKCore/SDKCore.test.ts index 5c00128..07908b1 100644 --- a/packages/core/src/SDKCore/SDKCore.test.ts +++ b/packages/core/src/SDKCore/SDKCore.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, it, expect, vi } from 'vitest'; -import { SDKConfig } from '#/SDKConfig'; +import { SDKConfig } from '../SDKConfig'; import { SDKCore } from '.'; import { mockIsLoggedIn, removeAt_expCookie } from '..'; diff --git a/packages/core/src/SDKCore/SDKCore.ts b/packages/core/src/SDKCore/SDKCore.ts index 5a24b25..47d2b7b 100644 --- a/packages/core/src/SDKCore/SDKCore.ts +++ b/packages/core/src/SDKCore/SDKCore.ts @@ -1,8 +1,8 @@ -import { UrlHelper } from '#/UrlHelper'; -import { SDKConfig } from '#/SDKConfig'; -import { UserInfo } from '#/SDKContext'; -import { RedirectHelper } from '#/RedirectHelper'; -import { getAccessTokenExpirationMoment } from '#/CookieHelpers'; +import { UrlHelper } from '../UrlHelper'; +import { SDKConfig } from '../SDKConfig'; +import { UserInfo } from '../SDKContext'; +import { RedirectHelper } from '../RedirectHelper'; +import { getAccessTokenExpirationMoment } from '../CookieHelpers'; /** A class containing framework-agnostic SDK methods */ export class SDKCore { diff --git a/packages/core/src/UrlHelper/UrlHelperTypes.ts b/packages/core/src/UrlHelper/UrlHelperTypes.ts index c3c54b3..8f40cc0 100644 --- a/packages/core/src/UrlHelper/UrlHelperTypes.ts +++ b/packages/core/src/UrlHelper/UrlHelperTypes.ts @@ -1,4 +1,4 @@ -import { SDKConfig } from '#/SDKConfig'; +import { SDKConfig } from '../SDKConfig'; /** A configuration object for the UrlHelper class. */ export type UrlHelperConfig = Pick< diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 470b091..2e21c1a 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -20,11 +20,7 @@ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - - "paths": { - "#/*": ["./src/*"] - } + "noFallthroughCasesInSwitch": true }, "include": ["src"] } diff --git a/packages/core/vite.config.ts b/packages/core/vite.config.ts index 8107ccc..a8e4813 100644 --- a/packages/core/vite.config.ts +++ b/packages/core/vite.config.ts @@ -15,17 +15,10 @@ export default defineConfig({ fileName: 'index', formats: ['es'], }, - rollupOptions: { - external: ['**/*.test.*'], - }, - }, - plugins: [dts()], - server: { - port: 3001, - }, - resolve: { - alias: { - '#': resolve(__dirname, './src'), - }, }, + plugins: [ + dts({ + exclude: ['**/*.test.ts'], + }), + ], }); diff --git a/packages/sdk-angular/README.md b/packages/sdk-angular/README.md index 0678eff..8768775 100644 --- a/packages/sdk-angular/README.md +++ b/packages/sdk-angular/README.md @@ -9,6 +9,7 @@ An SDK for using FusionAuth in Angular applications. - [FusionAuthService](#fusionauthservice) - [Pre-built buttons](#pre-built-buttons) - [State Parameter](#state-parameter) + - [SSR](#ssr) - [Known issues](#known-issues) - [Quickstart](#quickstart) - [Documentation](#documentation) @@ -174,6 +175,10 @@ pass any value you would like for the state parameter, it is often used to indic which page the user was on before redirecting to login or registration, so that the user can be returned to that location after a successful authentication. +#### SSR + +The SDK supports Angular applications using SSR. No additional configuration is needed. + ### Known Issues None. diff --git a/packages/sdk-angular/getSDKCore.js b/packages/sdk-angular/getSDKCore.js new file mode 100644 index 0000000..0e30e72 --- /dev/null +++ b/packages/sdk-angular/getSDKCore.js @@ -0,0 +1,71 @@ +/** + * This file is responsible for copying @fusionauth-sdk/core into this Angular package without transpiling it. + * This is to allow Angular's library builder to bundle the core code into dist. + * Further details here: https://github.com/FusionAuth/fusionauth-javascript-sdk/issues/84 + */ + +const fs = require('fs').promises; +const { existsSync } = require('fs'); +const path = require('path'); + +async function copyDirectory(src, dest) { + await fs.mkdir(dest, { recursive: true }); + + const srcDirContents = await fs.readdir(src, { withFileTypes: true }); + + for (const item of srcDirContents) { + if (item.isFile() && item.name.endsWith('.test.ts')) { + continue; + } + + const itemSrc = path.resolve(src, item.name); + const itemDest = path.resolve(dest, item.name); + + if (item.isDirectory()) { + await copyDirectory(itemSrc, itemDest); + } else if (item.isFile()) { + await fs.copyFile(itemSrc, itemDest); + } + } +} + +async function pruneDirectory(dir) { + if (!existsSync(dir)) { + // check if directory can be accessed before reading from it + return; + } + + try { + const directoryContents = await fs.readdir(dir, { + withFileTypes: true, + }); + + await Promise.all( + directoryContents.map(async item => { + const itemPath = path.resolve(dir, item.name); + + if (item.isDirectory()) { + await pruneDirectory(itemPath); + } else { + await fs.unlink(itemPath); + } + }), + ); + } catch (err) { + throw err; + } +} + +(async () => { + const coreSrc = '../core/src'; + const coreDest = 'projects/fusionauth-angular-sdk/src/sdkcore'; + + await pruneDirectory(coreDest); + await copyDirectory(coreSrc, coreDest) + .then(() => + console.log(`Successfully copied @fusionauth-sdk/core into ${coreDest}`), + ) + .catch(err => + console.error('Error copying core src directory for angular:', err), + ); +})(); diff --git a/packages/sdk-angular/package.json b/packages/sdk-angular/package.json index a306565..5ff807e 100644 --- a/packages/sdk-angular/package.json +++ b/packages/sdk-angular/package.json @@ -3,7 +3,8 @@ "version": "0.0.1", "scripts": { "ng": "ng", - "build": "ng build && cp README.md dist/fusionauth-angular-sdk/README.md", + "get-sdk-core": "node getSDKCore.js", + "build": "yarn get-sdk-core && ng build && cp README.md dist/fusionauth-angular-sdk/README.md", "test": "ng test --watch=false --browsers=ChromeHeadless", "test:watch": "ng test", "docs": "typedoc --plugin typedoc-plugin-markdown" diff --git a/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/SSRCookieAdapter.ts b/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/SSRCookieAdapter.ts index 776d1e7..1d844dd 100644 --- a/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/SSRCookieAdapter.ts +++ b/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/SSRCookieAdapter.ts @@ -1,7 +1,7 @@ -// import { CookieAdapter } from '@fusionauth-sdk/core'; +import { CookieAdapter } from '../sdkcore'; /** An adapter class that supports accessing cookies with SSR */ -export class SSRCookieAdapter /* implements CookieAdapter */ { +export class SSRCookieAdapter implements CookieAdapter { constructor(private isBrowser: boolean) {} at_exp(cookieName: string = 'app.at_exp') { diff --git a/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/core.ts b/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/core.ts deleted file mode 100644 index 7091095..0000000 --- a/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/core.ts +++ /dev/null @@ -1,266 +0,0 @@ -// @ts-nocheck -const u = Object.defineProperty; -const d = (r, e, t) => - e in r - ? u(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) - : (r[e] = t); -const i = (r, e, t) => (d(r, typeof e != 'symbol' ? e + '' : e, t), t); -class g { - constructor(e) { - i(this, 'serverUrl'); - i(this, 'clientId'); - i(this, 'redirectUri'); - i(this, 'scope'); - i(this, 'mePath'); - i(this, 'loginPath'); - i(this, 'registerPath'); - i(this, 'logoutPath'); - i(this, 'tokenRefreshPath'); - i(this, 'postLogoutRedirectUri'); - (this.serverUrl = e.serverUrl), - (this.clientId = e.clientId), - (this.redirectUri = e.redirectUri), - (this.scope = e.scope), - (this.postLogoutRedirectUri = e.postLogoutRedirectUri), - (this.mePath = e.mePath ?? '/app/me'), - (this.loginPath = e.loginPath ?? '/app/login'), - (this.registerPath = e.registerPath ?? '/app/register'), - (this.logoutPath = e.logoutPath ?? '/app/logout'), - (this.tokenRefreshPath = e.tokenRefreshPath ?? '/app/refresh'); - } - getMeUrl() { - return this.generateUrl(this.mePath); - } - getLoginUrl(e) { - return this.generateUrl(this.loginPath, { - client_id: this.clientId, - redirect_uri: this.redirectUri, - scope: this.scope, - state: e, - }); - } - getRegisterUrl(e) { - return this.generateUrl(this.registerPath, { - client_id: this.clientId, - redirect_uri: this.redirectUri, - scope: this.scope, - state: e, - }); - } - getLogoutUrl() { - return this.generateUrl(this.logoutPath, { - client_id: this.clientId, - post_logout_redirect_uri: this.postLogoutRedirectUri || this.redirectUri, - }); - } - getTokenRefreshUrl() { - return this.generateUrl(this.tokenRefreshPath, { - client_id: this.clientId, - }); - } - generateUrl(e, t) { - const s = new URL(this.serverUrl); - if (((s.pathname = e), t)) { - const o = this.generateURLSearchParams(t); - s.search = o.toString(); - } - return s; - } - generateURLSearchParams(e) { - const t = new URLSearchParams(); - return ( - Object.entries(e).forEach(([s, o]) => { - o && t.append(s, o); - }), - t - ); - } -} -class p { - constructor() { - i(this, 'REDIRECT_VALUE', 'fa-sdk-redirect-value'); - } - get storage() { - try { - return localStorage; - } catch { - return { - /* eslint-disable */ - setItem(e, t) {}, - getItem(e) {}, - removeItem(e) {}, - /* eslint-enable */ - }; - } - } - handlePreRedirect(e) { - const t = `${this.generateRandomString()}:${e ?? ''}`; - this.storage.setItem(this.REDIRECT_VALUE, t); - } - handlePostRedirect(e) { - const t = this.stateValue ?? void 0; - e == null || e(t), this.storage.removeItem(this.REDIRECT_VALUE); - } - get didRedirect() { - return !!this.storage.getItem(this.REDIRECT_VALUE); - } - get stateValue() { - const e = this.storage.getItem(this.REDIRECT_VALUE); - if (!e) return null; - const [, ...t] = e.split(':'); - return t.join(':'); - } - generateRandomString() { - const e = new Uint32Array(28); - return ( - window.crypto.getRandomValues(e), - Array.from(e, t => ('0' + t.toString(16)).substring(-2)).join('') - ); - } -} -function m(r = 'app.at_exp', e) { - if (e) return c(e.at_exp(r)); - let t; - try { - t = document.cookie; - } catch { - return ( - console.error( - 'Error accessing cookies in fusionauth. If you are using SSR you must configure the SDK with a cookie adapter', - ), - -1 - ); - } - const s = t - .split('; ') - .map(n => n.split('=')) - .find(([n]) => n === r), - o = s == null ? void 0 : s[1]; - return c(o); -} -function c(r) { - return r ? Number(r) * 1e3 : -1; -} -class U { - constructor(e) { - i(this, 'config'); - i(this, 'urlHelper'); - i(this, 'redirectHelper', new p()); - i(this, 'tokenExpirationTimeout'); - (this.config = e), - (this.urlHelper = new g({ - serverUrl: e.serverUrl, - clientId: e.clientId, - redirectUri: e.redirectUri, - scope: e.scope, - mePath: e.mePath, - loginPath: e.loginPath, - registerPath: e.registerPath, - logoutPath: e.logoutPath, - tokenRefreshPath: e.tokenRefreshPath, - postLogoutRedirectUri: e.postLogoutRedirectUri, - })), - this.scheduleTokenExpiration(); - } - startLogin(e) { - this.redirectHelper.handlePreRedirect(e), - window.location.assign(this.urlHelper.getLoginUrl(e)); - } - startRegister(e) { - this.redirectHelper.handlePreRedirect(e), - window.location.assign(this.urlHelper.getRegisterUrl(e)); - } - startLogout() { - window.location.assign(this.urlHelper.getLogoutUrl()); - } - async fetchUserInfo() { - const e = await fetch(this.urlHelper.getMeUrl(), { - credentials: 'include', - }); - if (!e.ok) - throw new Error( - `Unable to fetch userInfo in fusionauth. Request failed with status code ${e == null ? void 0 : e.status}`, - ); - return await e.json(); - } - async refreshToken() { - const e = await fetch(this.urlHelper.getTokenRefreshUrl(), { - method: 'POST', - credentials: 'include', - headers: { - 'Content-Type': 'text/plain', - }, - }); - if (!(e.status >= 200 && e.status < 300)) { - const t = - (await (e == null ? void 0 : e.text())) || - 'Error refreshing access token in fusionauth'; - throw new Error(t); - } - return this.scheduleTokenExpiration(), e; - } - initAutoRefresh() { - if (!this.isLoggedIn) return; - const t = (this.config.autoRefreshSecondsBeforeExpiry ?? 10) * 1e3, - s = /* @__PURE__ */ new Date().getTime(), - o = this.at_exp - t, - n = Math.max(o - s, 0); - return setTimeout(async () => { - let a, h; - try { - await this.refreshToken(), this.initAutoRefresh(); - } catch (l) { - (h = (a = this.config).onAutoRefreshFailure) == null || h.call(a, l); - } - }, n); - } - handlePostRedirect(e) { - this.isLoggedIn && - this.redirectHelper.didRedirect && - this.redirectHelper.handlePostRedirect(e); - } - get isLoggedIn() { - return this.at_exp > /* @__PURE__ */ new Date().getTime(); - } - /** The moment of access token expiration in milliseconds since epoch. */ - get at_exp() { - return m( - this.config.accessTokenExpireCookieName, - this.config.cookieAdapter, - ); - } - /** Schedules `onTokenExpiration` at moment of access token expiration. */ - scheduleTokenExpiration() { - clearTimeout(this.tokenExpirationTimeout); - const e = /* @__PURE__ */ new Date().getTime(), - t = this.at_exp - e; - t > 0 && - (this.tokenExpirationTimeout = setTimeout( - this.config.onTokenExpiration, - t, - )); - } -} -function f() { - const r = /* @__PURE__ */ new Date(); - r.setHours(r.getHours() + 1); - const e = r.getTime() / 1e3; - document.cookie = `app.at_exp=${e}`; -} -function P() { - document.cookie = 'app.at_exp=;expires=Thu, 01 Jan 1970 00:00:00 GMT'; -} -function w(r) { - const e = { - ...window.location, - assign: r.fn(), - }; - return r.spyOn(window, 'location', 'get').mockReturnValue(e), e; -} -export { - U as SDKCore, - f as mockIsLoggedIn, - w as mockWindowLocation, - P as removeAt_expCookie, -}; -//# sourceMappingURL=index.js.map diff --git a/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.spec.ts b/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.spec.ts index 5fd8260..cd7e155 100644 --- a/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.spec.ts +++ b/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.spec.ts @@ -5,7 +5,7 @@ import { take } from 'rxjs'; import { FusionAuthConfig } from './types'; import { FusionAuthService } from './fusion-auth.service'; import { FusionAuthModule } from './fusion-auth.module'; -import { mockIsLoggedIn, removeAt_expCookie } from './core'; +import { mockIsLoggedIn, removeAt_expCookie } from '../sdkcore'; const config: FusionAuthConfig = { clientId: 'a-client-id', diff --git a/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.ts b/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.ts index c438a7b..3db495f 100644 --- a/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.ts +++ b/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.ts @@ -2,7 +2,7 @@ import { Injectable, Inject, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; import { Observable, catchError, BehaviorSubject } from 'rxjs'; -import { SDKCore } from './core'; +import { SDKCore } from '../sdkcore'; import { SSRCookieAdapter } from './SSRCookieAdapter'; import { FusionAuthConfig, UserInfo } from './types'; import { FUSIONAUTH_SERVICE_CONFIG } from './injectionToken';