@finsweet/webflow-apps-utils 1.0.19 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/types/index.d.ts +0 -1
  2. package/dist/types/index.js +0 -1
  3. package/dist/ui/providers/GlobalProvider.svelte +0 -10
  4. package/dist/ui/stores/siteInfo.d.ts +2 -0
  5. package/dist/ui/utils/api/clipboard/handlePaste.js +1 -3
  6. package/dist/ui/utils/api/index.d.ts +0 -1
  7. package/dist/ui/utils/api/index.js +0 -1
  8. package/dist/ui/utils/index.d.ts +0 -1
  9. package/dist/ui/utils/index.js +0 -1
  10. package/dist/utils/api/clipboard/handlePaste.js +1 -3
  11. package/dist/utils/api/index.d.ts +0 -1
  12. package/dist/utils/api/index.js +0 -1
  13. package/dist/utils/browser-storage/sessionStorage.js +1 -3
  14. package/dist/utils/index.d.ts +0 -1
  15. package/dist/utils/index.js +0 -1
  16. package/dist/utils/webflow-canvas/attributes/getAllWebflowElementAttributes.js +1 -3
  17. package/dist/utils/webflow-canvas/attributes/getWebflowElementAttribute.js +1 -3
  18. package/dist/utils/webflow-canvas/attributes/removeWebflowElementAttribute.js +1 -3
  19. package/dist/utils/webflow-canvas/attributes/setWebflowElementAttribute.js +2 -4
  20. package/dist/utils/webflow-canvas/findInstanceElement.js +1 -3
  21. package/dist/utils/webflow-canvas/getAllPages.js +1 -3
  22. package/package.json +2 -2
  23. package/dist/types/auth.d.ts +0 -40
  24. package/dist/types/auth.js +0 -1
  25. package/dist/ui/utils/api/getFinsweetComponentsEnvironment.d.ts +0 -8
  26. package/dist/ui/utils/api/getFinsweetComponentsEnvironment.js +0 -66
  27. package/dist/ui/utils/auth/crossWindowLogin.d.ts +0 -5
  28. package/dist/ui/utils/auth/crossWindowLogin.js +0 -52
  29. package/dist/ui/utils/auth/index.d.ts +0 -45
  30. package/dist/ui/utils/auth/index.js +0 -178
  31. package/dist/utils/api/getFinsweetComponentsEnvironment.d.ts +0 -8
  32. package/dist/utils/api/getFinsweetComponentsEnvironment.js +0 -67
  33. package/dist/utils/logger/index.d.ts +0 -16
  34. package/dist/utils/logger/index.js +0 -39
@@ -1,4 +1,3 @@
1
- export * from './auth';
2
1
  export * from './dom';
3
2
  export * from './customCode';
4
3
  export * from './license';
@@ -1,4 +1,3 @@
1
- export * from './auth';
2
1
  export * from './dom';
3
2
  export * from './customCode';
4
3
  export * from './license';
@@ -1,6 +1,4 @@
1
1
  <script lang="ts">
2
- import { getFinsweetComponentsEnvironment } from '../../utils/index.js';
3
-
4
2
  import { createGlobalContext, setGlobalContext } from './globalContext.svelte';
5
3
  import type { GlobalProviderProps } from './types';
6
4
 
@@ -41,14 +39,6 @@
41
39
  // Create and set the global context
42
40
  const globalContext = createGlobalContext(mergedContexts, debug);
43
41
  setGlobalContext(globalContext);
44
- const { development } = getFinsweetComponentsEnvironment();
45
-
46
- // Development mode debugging
47
- if (development && typeof window !== 'undefined') {
48
- globalContext.subscribe((event) => {
49
- console.log('🌍 Global Context Event:', event);
50
- });
51
- }
52
42
  </script>
53
43
 
54
44
  {#if children}{@render children()}{/if}
@@ -4,6 +4,8 @@ export declare const siteInfo: import("svelte/store").Writable<{
4
4
  shortName: string;
5
5
  isPasswordProtected: boolean;
6
6
  isPrivateStaging: boolean;
7
+ workspaceId: string;
8
+ workspaceSlug: string;
7
9
  domains: Array<{
8
10
  url: string;
9
11
  lastPublished: string | null;
@@ -1,5 +1,3 @@
1
- import { getLogger } from '../../../../utils/logger';
2
- const logger = getLogger('webflow-apps-ui-utils');
3
1
  /**
4
2
  * Processes pasted component data to validate Finsweet components.
5
3
  */
@@ -38,7 +36,7 @@ export const handlePasteXSCP = (e, component) => {
38
36
  });
39
37
  }
40
38
  catch (error) {
41
- logger.error({}, 'handlePasteXSCP', error);
39
+ console.error({}, 'handlePasteXSCP failed with error', error);
42
40
  webflow.notify({
43
41
  type: 'Error',
44
42
  message: 'Invalid! You can only paste valid Finsweet Components.'
@@ -1,5 +1,4 @@
1
1
  export * from './checkIfAppModeIsDesign';
2
2
  export * from './clipboard';
3
3
  export * from './getAllAssets';
4
- export * from './getFinsweetComponentsEnvironment';
5
4
  export * from './insertWithXSCP';
@@ -1,5 +1,4 @@
1
1
  export * from './checkIfAppModeIsDesign';
2
2
  export * from './clipboard';
3
3
  export * from './getAllAssets';
4
- export * from './getFinsweetComponentsEnvironment';
5
4
  export * from './insertWithXSCP';
@@ -1,4 +1,3 @@
1
- export * from './auth';
2
1
  export * from './diff-mapper';
3
2
  export * from './helpers';
4
3
  export * from './color-utils';
@@ -1,4 +1,3 @@
1
- export * from './auth';
2
1
  export * from './diff-mapper';
3
2
  export * from './helpers';
4
3
  export * from './color-utils';
@@ -1,5 +1,3 @@
1
- import { getLogger } from '../../logger';
2
- const logger = getLogger('webflow-apps-ui-utils');
3
1
  /**
4
2
  * Processes pasted component data to validate Finsweet components.
5
3
  */
@@ -38,7 +36,7 @@ export const handlePasteXSCP = (e, component) => {
38
36
  });
39
37
  }
40
38
  catch (error) {
41
- logger.error({}, 'handlePasteXSCP', error);
39
+ console.error({}, 'handlePasteXSCP failed with error', error);
42
40
  webflow.notify({
43
41
  type: 'Error',
44
42
  message: 'Invalid! You can only paste valid Finsweet Components.'
@@ -1,5 +1,4 @@
1
1
  export * from './checkIfAppModeIsDesign';
2
2
  export * from './clipboard';
3
3
  export * from './getAllAssets';
4
- export * from './getFinsweetComponentsEnvironment';
5
4
  export * from './insertWithXSCP';
@@ -1,5 +1,4 @@
1
1
  export * from './checkIfAppModeIsDesign';
2
2
  export * from './clipboard';
3
3
  export * from './getAllAssets';
4
- export * from './getFinsweetComponentsEnvironment';
5
4
  export * from './insertWithXSCP';
@@ -1,8 +1,6 @@
1
1
  /**
2
2
  * Checks if sessionStorage is available in the current environment.
3
3
  */
4
- import { getLogger } from '../logger';
5
- const logger = getLogger('webflow-apps-ui-utils');
6
4
  export const isSessionStorageAvailable = () => {
7
5
  try {
8
6
  // Attempt to access sessionStorage
@@ -12,7 +10,7 @@ export const isSessionStorageAvailable = () => {
12
10
  return true;
13
11
  }
14
12
  catch (e) {
15
- logger.error({}, 'Error! window.sessionStorage is not available your browser setting, please check if you have disabled third party cookies and/or site data.', e);
13
+ console.error({}, 'Error! window.sessionStorage is not available your browser setting, please check if you have disabled third party cookies and/or site data.', e);
16
14
  return false;
17
15
  }
18
16
  };
@@ -4,7 +4,6 @@ export * from './browser-storage';
4
4
  export * from './custom-code';
5
5
  export * from './constants';
6
6
  export * from './helpers';
7
- export * from './logger';
8
7
  export * from './webflow';
9
8
  export * from './webflow-canvas';
10
9
  export type * from '../types';
@@ -4,6 +4,5 @@ export * from './browser-storage';
4
4
  export * from './custom-code';
5
5
  export * from './constants';
6
6
  export * from './helpers';
7
- export * from './logger';
8
7
  export * from './webflow';
9
8
  export * from './webflow-canvas';
@@ -1,5 +1,3 @@
1
- import { getLogger } from '../../logger';
2
- const logger = getLogger('webflow-apps-ui-utils');
3
1
  /**
4
2
  * Gets all custom attributes from a Webflow element.
5
3
  */
@@ -11,7 +9,7 @@ export const getAllWebflowElementAttributes = async (element) => {
11
9
  return null;
12
10
  }
13
11
  catch (error) {
14
- logger.error({}, 'Error in getAllWebflowElementAttributes:', error);
12
+ console.error({}, 'Error in getAllWebflowElementAttributes:', error);
15
13
  return null;
16
14
  }
17
15
  };
@@ -1,5 +1,3 @@
1
- import { getLogger } from '../../logger';
2
- const logger = getLogger('webflow-apps-ui-utils');
3
1
  /**
4
2
  * Gets the value of a custom attribute from a Webflow element.
5
3
  */
@@ -16,7 +14,7 @@ export const getWebflowElementAttribute = async (element, attributeName) => {
16
14
  return null;
17
15
  }
18
16
  catch (error) {
19
- logger.error({}, 'Error in getWebflowElementAttribute:', error);
17
+ console.error({}, 'Error in getWebflowElementAttribute:', error);
20
18
  return null;
21
19
  }
22
20
  };
@@ -1,5 +1,3 @@
1
- import { getLogger } from '../../logger';
2
- const logger = getLogger('webflow-apps-ui-utils');
3
1
  /**
4
2
  * Removes a custom attribute from a Webflow element.
5
3
  */
@@ -28,7 +26,7 @@ export const removeWebflowElementAttribute = async (element, attributeName, noti
28
26
  return;
29
27
  }
30
28
  catch (error) {
31
- logger.error({}, 'Error in removeWebflowElementAttribute:', error);
29
+ console.error('Error in removeWebflowElementAttribute:', error);
32
30
  return null;
33
31
  }
34
32
  };
@@ -1,6 +1,4 @@
1
- import { getLogger } from '../../logger';
2
1
  import { getWebflowElementAttribute } from './getWebflowElementAttribute';
3
- const logger = getLogger('webflow-apps-ui-utils');
4
2
  /**
5
3
  * Sets a custom attribute value on a Webflow element.
6
4
  */
@@ -8,7 +6,7 @@ export const setWebflowElementAttribute = async (element, attributeName, attribu
8
6
  try {
9
7
  const attributeExists = await checkAttribute(element, attributeName, attributeValue);
10
8
  if (attributeExists) {
11
- logger.error({}, `Attribute ${attributeName}="${attributeValue}" already exists on the element. Exiting`);
9
+ console.error(`Attribute ${attributeName}="${attributeValue}" already exists on the element. Exiting`);
12
10
  return;
13
11
  }
14
12
  if (element?.customAttributes) {
@@ -34,7 +32,7 @@ export const setWebflowElementAttribute = async (element, attributeName, attribu
34
32
  return;
35
33
  }
36
34
  catch (error) {
37
- logger.error({}, 'Error in setWebflowElementAttribute:', error);
35
+ console.error('Error in setWebflowElementAttribute:', error);
38
36
  return;
39
37
  }
40
38
  };
@@ -1,7 +1,5 @@
1
1
  import { noop } from '../helpers';
2
- import { getLogger } from '../logger';
3
2
  import { getWebflowElementAttribute } from './attributes';
4
- const logger = getLogger('utils');
5
3
  /**
6
4
  * Exits the current component context.
7
5
  */
@@ -132,7 +130,7 @@ export const findInstanceElement = async ({ targetIndex, instance, component, al
132
130
  return null;
133
131
  }
134
132
  catch (error) {
135
- logger.error({}, 'Error in findInstanceInsideOrOutsideComponent:', error);
133
+ console.error({}, 'Error in findInstanceInsideOrOutsideComponent:', error);
136
134
  throw error;
137
135
  }
138
136
  };
@@ -1,5 +1,3 @@
1
- import { getLogger } from '../logger';
2
- const logger = getLogger('webflow-apps-ui-utils');
3
1
  let pageStagingUrl;
4
2
  /**
5
3
  * Generates a slug for a page or folder.
@@ -80,7 +78,7 @@ export const getAllPages = async (pagesAndFolders, kind) => {
80
78
  return pagesOnly;
81
79
  }
82
80
  catch (error) {
83
- logger.error({}, 'getAllPages failed with an error:', error);
81
+ console.error({}, 'getAllPages failed with an error:', error);
84
82
  return [];
85
83
  }
86
84
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finsweet/webflow-apps-utils",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "Shared utilities for Webflow apps",
5
5
  "homepage": "https://github.com/finsweet/webflow-apps-utils",
6
6
  "repository": {
@@ -62,7 +62,7 @@
62
62
  "@types/node": "^22",
63
63
  "@vitest/browser": "3.2.3",
64
64
  "@vitest/coverage-v8": "3.2.3",
65
- "@webflow/designer-extension-typings": "^2.0.12",
65
+ "@webflow/designer-extension-typings": "latest",
66
66
  "eslint": "^9.18.0",
67
67
  "eslint-config-prettier": "^10.0.1",
68
68
  "eslint-plugin-simple-import-sort": "^12.1.1",
@@ -1,40 +0,0 @@
1
- export interface Subscription {
2
- id: string;
3
- product_id: string;
4
- price_id: string;
5
- status: string;
6
- canceled_at: number | null;
7
- current_period_end: number | null;
8
- current_period_start: number | null;
9
- metadata: SubscriptionMetadata;
10
- }
11
- export interface SubscriptionMetadata {
12
- siteId: string;
13
- rewardful: string;
14
- siteUrl: string;
15
- siteHostname: string;
16
- }
17
- export interface FinsweetAuth {
18
- access_token: string;
19
- expires_in: number;
20
- id_token: string;
21
- refresh_token: string;
22
- scope: string;
23
- token_type: string;
24
- expires: number;
25
- user: {
26
- userId: string;
27
- firstname: string;
28
- admin: boolean;
29
- auth0Id: string;
30
- premium: boolean;
31
- role: string;
32
- libraryAccess: ('canFavourite' | 'canManageLibrary')[] | null;
33
- folders: {
34
- favourites: number;
35
- configs: number;
36
- savedComponents: number;
37
- };
38
- };
39
- subscriptions: Subscription[];
40
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,8 +0,0 @@
1
- /**
2
- * Gets the Finsweet components environment configuration.
3
- */
4
- export declare const getFinsweetComponentsEnvironment: () => {
5
- development: boolean;
6
- coreScript: string;
7
- api: string;
8
- };
@@ -1,66 +0,0 @@
1
- import { COMPONENTS_CORE_SCRIPT, COMPONENTS_CORE_SCRIPT_LOCAL, COMPONENTS_SERVER_DEV_ENDPOINT, COMPONENTS_SERVER_DEV_ENDPOINT_LOCAL, COMPONENTS_SERVER_PROD_ENDPOINT, getLocalStorage } from '../../../utils';
2
- const DEV_MODE_KEY = 'fsComponentsDevMode';
3
- const DEV_MODE_SCRIPT_KEY = 'fsComponentsDevModeScript';
4
- const DEV_MODE_API_KEY = 'fsComponentsDevModeApi';
5
- const URL_DEV_MODE_KEY = 'dev';
6
- const URL_DEV_MODE_SCRIPT_KEY = 'script';
7
- const URL_DEV_MODE_API_KEY = 'api';
8
- let lastLogTime = 0;
9
- const LOG_THROTTLE_MS = 3000;
10
- /**
11
- * Gets a parameter value from the URL search params.
12
- */
13
- const getUrlParam = (key) => {
14
- if (typeof window === 'undefined')
15
- return null;
16
- try {
17
- const urlParams = new URLSearchParams(window.location.search);
18
- return urlParams.get(key);
19
- }
20
- catch (e) {
21
- console.error('Error getting URL parameter:', e);
22
- return null;
23
- }
24
- };
25
- /**
26
- * Throttled console log that only logs once within the throttle interval.
27
- */
28
- const throttledLog = (message, data) => {
29
- const now = Date.now();
30
- if (now - lastLogTime >= LOG_THROTTLE_MS) {
31
- console.log(message, data || '');
32
- lastLogTime = now;
33
- }
34
- };
35
- /**
36
- * Gets the Finsweet components environment configuration.
37
- */
38
- export const getFinsweetComponentsEnvironment = () => {
39
- const devFromUrl = getUrlParam(URL_DEV_MODE_KEY);
40
- const scriptFromUrl = getUrlParam(URL_DEV_MODE_SCRIPT_KEY);
41
- const apiFromUrl = getUrlParam(URL_DEV_MODE_API_KEY);
42
- const dev = devFromUrl !== null ? devFromUrl === 'true' : getLocalStorage(DEV_MODE_KEY) === 'true';
43
- let script = scriptFromUrl || getLocalStorage(DEV_MODE_SCRIPT_KEY) || COMPONENTS_CORE_SCRIPT;
44
- let api = dev
45
- ? apiFromUrl || getLocalStorage(DEV_MODE_API_KEY) || COMPONENTS_SERVER_DEV_ENDPOINT
46
- : COMPONENTS_SERVER_PROD_ENDPOINT;
47
- const isBrowser = typeof window !== 'undefined';
48
- const isLocalhost = isBrowser && window?.location?.hostname?.includes('localhost');
49
- //if localhost then use local scripts
50
- if (isLocalhost) {
51
- script = COMPONENTS_CORE_SCRIPT_LOCAL;
52
- api = COMPONENTS_SERVER_DEV_ENDPOINT_LOCAL;
53
- }
54
- const development = !!dev || isLocalhost;
55
- if (development) {
56
- throttledLog(`\n\nFinsweet Components Environment:
57
- - API: ${api}
58
- - Core script: ${script}
59
- - Development mode: ${development ? 'Yes' : 'No'}\n\n`);
60
- }
61
- return {
62
- development,
63
- coreScript: script,
64
- api
65
- };
66
- };
@@ -1,5 +0,0 @@
1
- import type { FinsweetAuth } from '../../../types';
2
- /**
3
- * Opens a popup window for cross-window authentication with Auth0.
4
- */
5
- export declare const crossWindowLogin: () => Promise<FinsweetAuth>;
@@ -1,52 +0,0 @@
1
- import { AUTH0_AUDIENCE, AUTH0_CLIENT_ID, AUTH0_LOGIN_URL, AUTH0_REDIRECT_URL, AUTH0_SCOPE, PROD_FINSWEEET_ACCOUNTS_ORIGIN } from '../../../utils/constants';
2
- /**
3
- * Opens a popup window for cross-window authentication with Auth0.
4
- */
5
- export const crossWindowLogin = () => {
6
- return new Promise((resolve, reject) => {
7
- const timeout = setTimeout(() => {
8
- window.removeEventListener('message', handleMessage);
9
- reject(new Error('Login timed out'));
10
- }, 30000);
11
- const handleMessage = (e) => {
12
- if (e.origin !== PROD_FINSWEEET_ACCOUNTS_ORIGIN) {
13
- return;
14
- }
15
- clearTimeout(timeout);
16
- window.removeEventListener('message', handleMessage);
17
- if (e.data && e.data.access_token) {
18
- resolve(e.data);
19
- }
20
- else {
21
- reject(new Error('Invalid data received'));
22
- }
23
- };
24
- window.addEventListener('message', handleMessage);
25
- // Open popup
26
- const { outerWidth, outerHeight, screenX, screenY } = window;
27
- const width = 512;
28
- const height = outerHeight / 2;
29
- const left = screenX + (outerWidth - width) / 2;
30
- const top = screenY + (outerHeight - height) / 2;
31
- const finsweetUrl = new URL(AUTH0_LOGIN_URL);
32
- finsweetUrl.searchParams.set('response_type', 'code');
33
- finsweetUrl.searchParams.set('client_id', AUTH0_CLIENT_ID);
34
- finsweetUrl.searchParams.set('redirect_uri', AUTH0_REDIRECT_URL);
35
- finsweetUrl.searchParams.set('scope', AUTH0_SCOPE);
36
- finsweetUrl.searchParams.set('audience', AUTH0_AUDIENCE);
37
- const popup = window.open(finsweetUrl.toString(), 'Finsweet Account', `width=${width},height=${height},left=${left},top=${top}`);
38
- if (!popup) {
39
- clearTimeout(timeout);
40
- reject(new Error('Failed to open popup'));
41
- }
42
- // Check if popup is closed
43
- const checkPopupClosed = setInterval(() => {
44
- if (popup?.closed) {
45
- clearInterval(checkPopupClosed);
46
- clearTimeout(timeout);
47
- window.removeEventListener('message', handleMessage);
48
- reject(new Error('Login popup was closed'));
49
- }
50
- }, 1000);
51
- });
52
- };
@@ -1,45 +0,0 @@
1
- import type { FinsweetAuth } from '../../../types';
2
- /**
3
- * Store for the Finsweet user data.
4
- */
5
- export declare const finsweetUser: import("svelte/store").Writable<FinsweetAuth | null>;
6
- export declare const isLoggingIn: import("svelte/store").Writable<boolean>;
7
- /**
8
- * Fetches user subscriptions from the server.
9
- */
10
- export declare const getSubscriptions: (token: string) => Promise<any>;
11
- /**
12
- * Fetches user data from the authentication server.
13
- */
14
- export declare const getUser: (token: string) => Promise<FinsweetAuth["user"]>;
15
- /**
16
- * Handles user login authentication flow.
17
- */
18
- export declare const handleLogin: () => Promise<void>;
19
- /**
20
- * Handles user logout and cleanup.
21
- */
22
- export declare const handleLogout: () => Promise<void>;
23
- /**
24
- * Checks if the user is logged in by validating stored session.
25
- */
26
- export declare const checkInitialLoginState: () => Promise<void>;
27
- /**
28
- * Sets a cookie with security options.
29
- */
30
- export declare const setCookie: (name: string, value: string, options?: {}) => void;
31
- /**
32
- * Gets a cookie value by name.
33
- */
34
- export declare const getCookie: (name: string) => string | undefined;
35
- /**
36
- * Deletes a cookie by name.
37
- */
38
- export declare const deleteCookie: (name: string, options?: {}) => void;
39
- /**
40
- * Checks if the user has access to a specific feature.
41
- */
42
- export declare const canAccessFeature: (loggedIn: boolean, access: FinsweetAuth["user"]["libraryAccess"] | null | undefined, required: FinsweetAuth["user"]["libraryAccess"]) => {
43
- granted: boolean;
44
- message: string;
45
- };
@@ -1,178 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */
2
- import Cookies from 'js-cookie';
3
- import { get, writable } from 'svelte/store';
4
- import { getLocalStorage, removeLocalStorage, setLocalStorage } from '../../../utils';
5
- import { FINSWEET_SUBSCRIPTIONS_ENDPOINT, FINSWEET_SUBSCRIPTIONS_ENDPOINT_STAGING } from '../../../utils/constants';
6
- import { getFinsweetComponentsEnvironment } from '../api';
7
- import { crossWindowLogin } from './crossWindowLogin';
8
- /**
9
- * Store for the Finsweet user data.
10
- */
11
- export const finsweetUser = writable();
12
- export const isLoggingIn = writable();
13
- const checkLibraryAccess = (user, subscriptions) => {
14
- // const fullAccess: FinsweetAuth['user']['libraryAccess'] = ['canFavourite', 'canManageLibrary'];
15
- // if (user.admin || user.role === 'agency') {
16
- // return fullAccess;
17
- // }
18
- // // cutoff date timestamp for June 5, 2024 00:00:00 UTC
19
- // const JUNE_5_2025 = 1749081600000;
20
- // if (subscriptions && subscriptions.length > 0) {
21
- // const hasFullAccess = subscriptions.some((subscription) => {
22
- // return (
23
- // subscription.status === 'active' &&
24
- // subscription.current_period_start &&
25
- // // subscription started must be before June 5, 2025 for full access
26
- // subscription.current_period_start * 1000 < JUNE_5_2025
27
- // );
28
- // });
29
- // if (hasFullAccess) return fullAccess;
30
- // }
31
- //TODO: Enable when RBAC is ready
32
- return ['canFavourite'];
33
- };
34
- /**
35
- * Fetches user subscriptions from the server.
36
- */
37
- export const getSubscriptions = async (token) => {
38
- try {
39
- const { development } = getFinsweetComponentsEnvironment();
40
- const endpoint = development
41
- ? FINSWEET_SUBSCRIPTIONS_ENDPOINT_STAGING
42
- : FINSWEET_SUBSCRIPTIONS_ENDPOINT;
43
- const response = await fetch(endpoint, {
44
- headers: {
45
- Authorization: `Bearer ${token}`
46
- }
47
- });
48
- const data = await response.json();
49
- return data;
50
- }
51
- catch (error) {
52
- console.error('Failed to fetch subscriptions', error);
53
- return [];
54
- }
55
- };
56
- /**
57
- * Fetches user data from the authentication server.
58
- */
59
- export const getUser = async (token) => {
60
- const response = await fetch('/auth0/verify', {
61
- headers: {
62
- Authorization: `Bearer ${token}`,
63
- 'Content-Type': 'application/json'
64
- }
65
- });
66
- const data = await response.json();
67
- return data;
68
- };
69
- /**
70
- * Handles user login authentication flow.
71
- */
72
- export const handleLogin = async () => {
73
- if (get(isLoggingIn))
74
- return;
75
- try {
76
- isLoggingIn.set(true);
77
- const response = await crossWindowLogin();
78
- const [user, subscriptions] = await Promise.all([
79
- getUser(response.access_token),
80
- getSubscriptions(response.access_token)
81
- ]);
82
- if (user?.userId) {
83
- const libraryAccess = checkLibraryAccess(user, subscriptions);
84
- finsweetUser.set({ ...response, user: { ...user, libraryAccess }, subscriptions });
85
- setLocalStorage('finsweetComponentsAuth', JSON.stringify(response));
86
- setLocalStorage('finsweetComponentsAuthToken', response.access_token);
87
- //dispatch login event
88
- const event = new CustomEvent('finsweetLoginUpdate', { detail: { user } });
89
- document.dispatchEvent(event);
90
- return;
91
- }
92
- throw new Error('User data is invalid');
93
- }
94
- catch (error) {
95
- const err = error;
96
- console.error('Login failed:', err.message);
97
- finsweetUser.set(null);
98
- webflow.notify({
99
- type: 'Error',
100
- message: 'Login failed. Please try again or contact support.'
101
- });
102
- }
103
- finally {
104
- isLoggingIn.set(false);
105
- }
106
- };
107
- /**
108
- * Handles user logout and cleanup.
109
- */
110
- export const handleLogout = async () => {
111
- removeLocalStorage('finsweetComponentsAuth');
112
- removeLocalStorage('finsweetComponentsAuthToken');
113
- finsweetUser.set(null);
114
- //dispatch login event
115
- const event = new CustomEvent('finsweetLogoutUpdate', { detail: { user: null } });
116
- document.dispatchEvent(event);
117
- await new Promise((resolve) => setTimeout(resolve, 1000));
118
- };
119
- /**
120
- * Checks if the user is logged in by validating stored session.
121
- */
122
- export const checkInitialLoginState = async () => {
123
- const session = getLocalStorage('finsweetComponentsAuth');
124
- if (session) {
125
- const parsedSession = JSON.parse(session);
126
- // Check if the current time is greater than the expiration time
127
- if (Date.now() > parsedSession.expires) {
128
- await handleLogout();
129
- return;
130
- }
131
- const [user, subscriptions] = await Promise.all([
132
- getUser(parsedSession.access_token),
133
- getSubscriptions(parsedSession.access_token)
134
- ]);
135
- if (user?.userId) {
136
- const libraryAccess = checkLibraryAccess(user, subscriptions);
137
- finsweetUser.set({ ...parsedSession, user: { ...user, libraryAccess }, subscriptions });
138
- return;
139
- }
140
- await handleLogout();
141
- return;
142
- }
143
- await handleLogout();
144
- };
145
- /**
146
- * Sets a cookie with security options.
147
- */
148
- export const setCookie = (name, value, options = {}) => {
149
- Cookies.set(name, value, { sameSite: 'None', secure: true, ...options });
150
- };
151
- /**
152
- * Gets a cookie value by name.
153
- */
154
- export const getCookie = (name) => {
155
- return Cookies.get(name);
156
- };
157
- /**
158
- * Deletes a cookie by name.
159
- */
160
- export const deleteCookie = (name, options = {}) => {
161
- Cookies.remove(name, options);
162
- };
163
- /**
164
- * Checks if the user has access to a specific feature.
165
- */
166
- export const canAccessFeature = (loggedIn, access, required) => {
167
- if (!loggedIn) {
168
- return {
169
- granted: false,
170
- message: "This feature is available only when you're logged in."
171
- };
172
- }
173
- if (required && required.every((r) => access?.includes(r))) {
174
- return { granted: true, message: '' };
175
- }
176
- // no message for logged in users, so tooltips are not shown
177
- return { granted: false, message: '' };
178
- };
@@ -1,8 +0,0 @@
1
- /**
2
- * Gets the Finsweet components environment configuration.
3
- */
4
- export declare const getFinsweetComponentsEnvironment: () => {
5
- development: boolean;
6
- coreScript: string;
7
- api: string;
8
- };
@@ -1,67 +0,0 @@
1
- import { getLocalStorage } from '../browser-storage';
2
- import { COMPONENTS_CORE_SCRIPT, COMPONENTS_CORE_SCRIPT_LOCAL, COMPONENTS_SERVER_DEV_ENDPOINT, COMPONENTS_SERVER_DEV_ENDPOINT_LOCAL, COMPONENTS_SERVER_PROD_ENDPOINT } from '../constants';
3
- const DEV_MODE_KEY = 'fsComponentsDevMode';
4
- const DEV_MODE_SCRIPT_KEY = 'fsComponentsDevModeScript';
5
- const DEV_MODE_API_KEY = 'fsComponentsDevModeApi';
6
- const URL_DEV_MODE_KEY = 'dev';
7
- const URL_DEV_MODE_SCRIPT_KEY = 'script';
8
- const URL_DEV_MODE_API_KEY = 'api';
9
- let lastLogTime = 0;
10
- const LOG_THROTTLE_MS = 3000;
11
- /**
12
- * Gets a parameter value from the URL search params.
13
- */
14
- const getUrlParam = (key) => {
15
- if (typeof window === 'undefined')
16
- return null;
17
- try {
18
- const urlParams = new URLSearchParams(window.location.search);
19
- return urlParams.get(key);
20
- }
21
- catch (e) {
22
- console.error('Error getting URL parameter:', e);
23
- return null;
24
- }
25
- };
26
- /**
27
- * Throttled console log that only logs once within the throttle interval.
28
- */
29
- const throttledLog = (message, data) => {
30
- const now = Date.now();
31
- if (now - lastLogTime >= LOG_THROTTLE_MS) {
32
- console.log(message, data || '');
33
- lastLogTime = now;
34
- }
35
- };
36
- /**
37
- * Gets the Finsweet components environment configuration.
38
- */
39
- export const getFinsweetComponentsEnvironment = () => {
40
- const devFromUrl = getUrlParam(URL_DEV_MODE_KEY);
41
- const scriptFromUrl = getUrlParam(URL_DEV_MODE_SCRIPT_KEY);
42
- const apiFromUrl = getUrlParam(URL_DEV_MODE_API_KEY);
43
- const dev = devFromUrl !== null ? devFromUrl === 'true' : getLocalStorage(DEV_MODE_KEY) === 'true';
44
- let script = scriptFromUrl || getLocalStorage(DEV_MODE_SCRIPT_KEY) || COMPONENTS_CORE_SCRIPT;
45
- let api = dev
46
- ? apiFromUrl || getLocalStorage(DEV_MODE_API_KEY) || COMPONENTS_SERVER_DEV_ENDPOINT
47
- : COMPONENTS_SERVER_PROD_ENDPOINT;
48
- const isBrowser = typeof window !== 'undefined';
49
- const isLocalhost = isBrowser && window?.location?.hostname?.includes('localhost');
50
- //if localhost then use local scripts
51
- if (isLocalhost) {
52
- script = COMPONENTS_CORE_SCRIPT_LOCAL;
53
- api = COMPONENTS_SERVER_DEV_ENDPOINT_LOCAL;
54
- }
55
- const development = !!dev || isLocalhost;
56
- if (development) {
57
- throttledLog(`\n\nFinsweet Components Environment:
58
- - API: ${api}
59
- - Core script: ${script}
60
- - Development mode: ${development ? 'Yes' : 'No'}\n\n`);
61
- }
62
- return {
63
- development,
64
- coreScript: script,
65
- api
66
- };
67
- };
@@ -1,16 +0,0 @@
1
- export type LoggerContext = string & Record<never, never>;
2
- export interface LogData {
3
- [key: string]: any;
4
- }
5
- export type LogLevel = 'error' | 'warn' | 'info' | 'log' | 'debug';
6
- export interface Logger {
7
- error: (context: LogData, message: string, ...args: any[]) => void;
8
- warn: (context: LogData, message: string, ...args: any[]) => void;
9
- info: (context: LogData, message: string, ...args: any[]) => void;
10
- log: (context: LogData, message: string, ...args: any[]) => void;
11
- debug: (context: LogData, message: string, ...args: any[]) => void;
12
- }
13
- /**
14
- * Create a logger instance for the given context
15
- */
16
- export declare function getLogger(context: LoggerContext): Logger;
@@ -1,39 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { getFinsweetComponentsEnvironment } from '../api';
3
- /**
4
- * Create a logger instance for the given context
5
- */
6
- export function getLogger(context) {
7
- const createLogMethod = (level) => {
8
- return (logContext, message, ...args) => {
9
- const { development } = getFinsweetComponentsEnvironment();
10
- if (development) {
11
- const contextPrefix = `[${context}]`;
12
- const consoleMethod = level === 'log' ? 'log' : level;
13
- if (typeof console[consoleMethod] === 'function') {
14
- console[consoleMethod](contextPrefix, message, logContext, ...args);
15
- }
16
- else {
17
- console.log(contextPrefix, message, logContext, ...args);
18
- }
19
- }
20
- // In production, show warnings in console
21
- if (!development && level === 'warn') {
22
- const contextPrefix = `[${context}]`;
23
- console.warn(contextPrefix, message, logContext, ...args);
24
- }
25
- // In production, only send errors to LogRocket
26
- if (!development && level === 'error') {
27
- const contextPrefix = `[${context}]`;
28
- console.error(contextPrefix, message, logContext, ...args);
29
- }
30
- };
31
- };
32
- return {
33
- error: createLogMethod('error'),
34
- warn: createLogMethod('warn'),
35
- info: createLogMethod('info'),
36
- log: createLogMethod('log'),
37
- debug: createLogMethod('debug')
38
- };
39
- }