@spur.us/monocle-react 1.1.1-canary.v20250707133917 → 1.1.1-canary.v20250918205642

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @spur.us/monocle-react@1.1.1-canary.v20250707133917 build /home/runner/work/javascript/javascript/packages/monocle-react
2
+ > @spur.us/monocle-react@1.1.1-canary.v20250918205642 build /home/runner/work/javascript/javascript/packages/monocle-react
3
3
  > tsup
4
4
 
5
5
  CLI Building entry: {"index":"src/index.ts"}
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  CJS Build start
12
12
  ESM Build start
13
- CJS dist/index.js 6.08 KB
14
- CJS dist/index.js.map 10.07 KB
15
- CJS ⚡️ Build success in 43ms
16
13
  ESM dist/index.mjs 4.27 KB
17
- ESM dist/index.mjs.map 9.92 KB
18
- ESM ⚡️ Build success in 46ms
14
+ ESM dist/index.mjs.map 9.85 KB
15
+ ESM ⚡️ Build success in 39ms
16
+ CJS dist/index.js 6.07 KB
17
+ CJS dist/index.js.map 9.99 KB
18
+ CJS ⚡️ Build success in 40ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 2242ms
20
+ DTS ⚡️ Build success in 2383ms
21
21
  DTS dist/index.d.ts 1.29 KB
22
22
  DTS dist/index.d.mts 1.29 KB
package/dist/index.js CHANGED
@@ -109,28 +109,9 @@ var MonocleProviderComponent = ({
109
109
  try {
110
110
  setIsLoading(true);
111
111
  setError(null);
112
- await loadScript();
113
112
  if (window.MCL) {
114
- let timeoutId = null;
115
- await window.MCL.configure({
116
- onAssessment: (assessment2) => {
117
- if (timeoutId) {
118
- clearTimeout(timeoutId);
119
- }
120
- setAssessment(assessment2);
121
- setIsLoading(false);
122
- }
123
- });
124
- const existingAssessment = window.MCL.getAssessment();
125
- if (existingAssessment) {
126
- setAssessment(existingAssessment);
127
- setIsLoading(false);
128
- } else {
129
- timeoutId = setTimeout(() => {
130
- setError(new Error("Assessment timeout - MCL did not respond within 30 seconds"));
131
- setIsLoading(false);
132
- }, 3e4);
133
- }
113
+ await window.MCL.refresh();
114
+ setIsLoading(false);
134
115
  } else {
135
116
  throw new Error("MCL object not found on window");
136
117
  }
@@ -140,17 +121,40 @@ var MonocleProviderComponent = ({
140
121
  );
141
122
  setIsLoading(false);
142
123
  }
143
- }, [publishableKey, domain]);
124
+ }, []);
144
125
  (0, import_react2.useEffect)(() => {
126
+ const initializeMCL = async () => {
127
+ try {
128
+ await loadScript();
129
+ if (window.MCL) {
130
+ await window.MCL.configure({
131
+ onAssessment: (assessment2) => {
132
+ setAssessment(assessment2);
133
+ setIsLoading(false);
134
+ }
135
+ });
136
+ const existingAssessment = window.MCL.getAssessment();
137
+ if (existingAssessment) {
138
+ setAssessment(existingAssessment);
139
+ setIsLoading(false);
140
+ }
141
+ }
142
+ } catch (err) {
143
+ setError(
144
+ err instanceof Error ? err : new Error("Failed to initialize MCL")
145
+ );
146
+ setIsLoading(false);
147
+ }
148
+ };
145
149
  if (!assessment) {
146
- refresh();
150
+ initializeMCL();
147
151
  }
148
152
  return () => {
149
153
  if (window.MCL) {
150
154
  window.MCL.configure({ onAssessment: void 0 });
151
155
  }
152
156
  };
153
- }, [publishableKey, domain, assessment, refresh]);
157
+ }, [publishableKey, domain]);
154
158
  const contextValue = (0, import_react2.useMemo)(
155
159
  () => ({ assessment, refresh, isLoading, error }),
156
160
  [assessment, refresh, isLoading, error]
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/contexts/MonocleProvider.tsx","../src/utils/useMaxAllowedInstancesGuard.tsx","../src/constants.ts"],"sourcesContent":["export * from './contexts';\n\nexport type { MonocleProviderProps } from './types';\n","import React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useMemo,\n useCallback,\n} from 'react';\nimport { withMaxAllowedInstancesGuard } from '../utils';\nimport { MonocleProviderProps } from '../types';\nimport { DOMAIN } from '../constants';\ninterface MonocleContextType {\n assessment: string | undefined;\n refresh: () => void;\n isLoading: boolean;\n error: Error | null;\n}\n\nconst MonocleContext = createContext<MonocleContextType | null>(null);\n\nconst MonocleProviderComponent: React.FC<MonocleProviderProps> = ({\n children,\n publishableKey,\n domain = DOMAIN,\n}) => {\n const [assessment, setAssessment] = useState<string | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const loadScript = () => {\n return new Promise<void>((resolve, reject) => {\n const existingScript = document.getElementById('_mcl');\n if (existingScript) {\n // If script exists but hasn't loaded yet, wait for it\n if (!window.MCL) {\n existingScript.onload = () => resolve();\n existingScript.onerror = () =>\n reject(new Error('Failed to load Monocle script'));\n } else {\n resolve();\n }\n return;\n }\n\n const script = document.createElement('script');\n script.id = '_mcl';\n script.async = true;\n script.src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;\n script.onload = () => {\n resolve();\n };\n script.onerror = (_e) => {\n console.error('MonocleProvider: Script failed to load');\n reject(new Error('Failed to load Monocle script'));\n };\n document.head.appendChild(script);\n });\n };\n\n const refresh = useCallback(async () => {\n try {\n setIsLoading(true);\n setError(null);\n await loadScript();\n if (window.MCL) {\n let timeoutId: NodeJS.Timeout | null = null;\n \n // Configure MCL with our callback to receive assessment updates\n await window.MCL.configure({\n onAssessment: (assessment: string) => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n setAssessment(assessment);\n setIsLoading(false);\n },\n });\n\n // Check if assessment is already available\n const existingAssessment = window.MCL.getAssessment();\n if (existingAssessment) {\n setAssessment(existingAssessment);\n setIsLoading(false);\n } else {\n // Set a timeout in case the assessment never comes\n timeoutId = setTimeout(() => {\n setError(new Error('Assessment timeout - MCL did not respond within 30 seconds'));\n setIsLoading(false);\n }, 30000);\n }\n } else {\n throw new Error('MCL object not found on window');\n }\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error('Unknown error occurred')\n );\n setIsLoading(false);\n }\n }, [publishableKey, domain]);\n\n useEffect(() => {\n // Only refresh if the publishableKey changes and we don't already have an assessment\n if (!assessment) {\n refresh();\n }\n \n // Cleanup function to reset callback on unmount\n return () => {\n if (window.MCL) {\n window.MCL.configure({ onAssessment: undefined });\n }\n };\n }, [publishableKey, domain, assessment, refresh]);\n\n const contextValue = useMemo(\n () => ({ assessment, refresh, isLoading, error }),\n [assessment, refresh, isLoading, error]\n );\n\n return (\n <MonocleContext.Provider value={contextValue}>\n {children}\n </MonocleContext.Provider>\n );\n};\n\nexport const MonocleProvider = withMaxAllowedInstancesGuard(\n MonocleProviderComponent,\n 'MonocleProvider',\n 'Only one instance of MonocleProvider is allowed'\n);\n\n/**\n * Hook to access the Monocle context.\n *\n * @returns {MonocleContextType} The Monocle context containing assessment data, loading state, and error information\n * @throws {Error} When used outside of a MonocleProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { assessment, isLoading, error, refresh } = useMonocle();\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return <div>Assessment: {assessment}</div>;\n * }\n * ```\n */\nexport const useMonocle = () => {\n const context = useContext(MonocleContext);\n if (!context) {\n throw new Error('useMonocle must be used within a MonocleProvider');\n }\n return context;\n};\n","import React from 'react';\n\nconst instanceCounter = new Map<string, number>();\n\n/**\n * A React hook that ensures a component is not instantiated more than a specified number of times.\n * Throws an error if the maximum number of instances is exceeded.\n *\n * @param name - A unique identifier for the component type\n * @param error - The error message to display if the maximum count is exceeded\n * @param maxCount - The maximum number of allowed instances (defaults to 1)\n * @throws Error when the maximum number of instances is exceeded\n *\n * @example\n * ```tsx\n * const MyComponent = () => {\n * useMaxAllowedInstancesGuard('MyComponent', 'Only one instance of MyComponent is allowed');\n * return <div>My Component</div>;\n * };\n * ```\n */\nexport function useMaxAllowedInstancesGuard(\n name: string,\n error: string,\n maxCount = 1\n): void {\n React.useEffect(() => {\n const count = instanceCounter.get(name) || 0;\n if (count === maxCount) {\n throw new Error(error);\n }\n instanceCounter.set(name, count + 1);\n\n return () => {\n const currentCount = instanceCounter.get(name) || 0;\n instanceCounter.set(name, Math.max(0, currentCount - 1));\n };\n }, []);\n}\n\n/**\n * A higher-order component that wraps a component with instance count protection.\n * This HOC ensures that the wrapped component cannot be instantiated more than once\n * (or a specified number of times) in the application.\n *\n * @param WrappedComponent - The component to wrap with instance count protection\n * @param name - A unique identifier for the component type\n * @param error - The error message to display if the maximum count is exceeded\n * @returns A new component with instance count protection\n *\n * @example\n * ```tsx\n * const ProtectedComponent = withMaxAllowedInstancesGuard(\n * MyComponent,\n * 'MyComponent',\n * 'Only one instance of MyComponent is allowed'\n * );\n * ```\n */\nexport function withMaxAllowedInstancesGuard<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n name: string,\n error: string\n): React.ComponentType<P> {\n const displayName =\n WrappedComponent.displayName ||\n WrappedComponent.name ||\n name ||\n 'Component';\n\n const Hoc: React.FC<P> = (props) => {\n useMaxAllowedInstancesGuard(name, error);\n return <WrappedComponent {...props} />;\n };\n\n Hoc.displayName = `withMaxAllowedInstancesGuard(${displayName})`;\n return Hoc;\n}\n","export const DOMAIN = 'mcl.spur.us';\n"],"mappings":"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","names":["import_react","React","import_jsx_runtime","assessment"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/contexts/MonocleProvider.tsx","../src/utils/useMaxAllowedInstancesGuard.tsx","../src/constants.ts"],"sourcesContent":["export * from './contexts';\n\nexport type { MonocleProviderProps } from './types';\n","import React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useMemo,\n useCallback,\n} from 'react';\nimport { withMaxAllowedInstancesGuard } from '../utils';\nimport { MonocleProviderProps } from '../types';\nimport { DOMAIN } from '../constants';\ninterface MonocleContextType {\n assessment: string | undefined;\n refresh: () => void;\n isLoading: boolean;\n error: Error | null;\n}\n\nconst MonocleContext = createContext<MonocleContextType | null>(null);\n\nconst MonocleProviderComponent: React.FC<MonocleProviderProps> = ({\n children,\n publishableKey,\n domain = DOMAIN,\n}) => {\n const [assessment, setAssessment] = useState<string | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const loadScript = () => {\n return new Promise<void>((resolve, reject) => {\n const existingScript = document.getElementById('_mcl');\n if (existingScript) {\n // If script exists but hasn't loaded yet, wait for it\n if (!window.MCL) {\n existingScript.onload = () => resolve();\n existingScript.onerror = () =>\n reject(new Error('Failed to load Monocle script'));\n } else {\n resolve();\n }\n return;\n }\n\n const script = document.createElement('script');\n script.id = '_mcl';\n script.async = true;\n script.src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;\n script.onload = () => {\n resolve();\n };\n script.onerror = (_e) => {\n console.error('MonocleProvider: Script failed to load');\n reject(new Error('Failed to load Monocle script'));\n };\n document.head.appendChild(script);\n });\n };\n\n const refresh = useCallback(async () => {\n try {\n setIsLoading(true);\n setError(null);\n \n if (window.MCL) {\n await window.MCL.refresh();\n setIsLoading(false);\n } else {\n throw new Error('MCL object not found on window');\n }\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error('Unknown error occurred')\n );\n setIsLoading(false);\n }\n }, []);\n\n useEffect(() => {\n const initializeMCL = async () => {\n try {\n await loadScript();\n if (window.MCL) {\n // Configure MCL with our callback to receive assessment updates\n await window.MCL.configure({\n onAssessment: (assessment: string) => {\n setAssessment(assessment);\n setIsLoading(false);\n },\n });\n\n // Check if assessment is already available\n const existingAssessment = window.MCL.getAssessment();\n if (existingAssessment) {\n setAssessment(existingAssessment);\n setIsLoading(false);\n }\n }\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error('Failed to initialize MCL')\n );\n setIsLoading(false);\n }\n };\n\n // Only initialize if we don't already have an assessment\n if (!assessment) {\n initializeMCL();\n }\n \n // Cleanup function to reset callback on unmount\n return () => {\n if (window.MCL) {\n window.MCL.configure({ onAssessment: undefined });\n }\n };\n }, [publishableKey, domain]);\n\n const contextValue = useMemo(\n () => ({ assessment, refresh, isLoading, error }),\n [assessment, refresh, isLoading, error]\n );\n\n return (\n <MonocleContext.Provider value={contextValue}>\n {children}\n </MonocleContext.Provider>\n );\n};\n\nexport const MonocleProvider = withMaxAllowedInstancesGuard(\n MonocleProviderComponent,\n 'MonocleProvider',\n 'Only one instance of MonocleProvider is allowed'\n);\n\n/**\n * Hook to access the Monocle context.\n *\n * @returns {MonocleContextType} The Monocle context containing assessment data, loading state, and error information\n * @throws {Error} When used outside of a MonocleProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { assessment, isLoading, error, refresh } = useMonocle();\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return <div>Assessment: {assessment}</div>;\n * }\n * ```\n */\nexport const useMonocle = () => {\n const context = useContext(MonocleContext);\n if (!context) {\n throw new Error('useMonocle must be used within a MonocleProvider');\n }\n return context;\n};\n","import React from 'react';\n\nconst instanceCounter = new Map<string, number>();\n\n/**\n * A React hook that ensures a component is not instantiated more than a specified number of times.\n * Throws an error if the maximum number of instances is exceeded.\n *\n * @param name - A unique identifier for the component type\n * @param error - The error message to display if the maximum count is exceeded\n * @param maxCount - The maximum number of allowed instances (defaults to 1)\n * @throws Error when the maximum number of instances is exceeded\n *\n * @example\n * ```tsx\n * const MyComponent = () => {\n * useMaxAllowedInstancesGuard('MyComponent', 'Only one instance of MyComponent is allowed');\n * return <div>My Component</div>;\n * };\n * ```\n */\nexport function useMaxAllowedInstancesGuard(\n name: string,\n error: string,\n maxCount = 1\n): void {\n React.useEffect(() => {\n const count = instanceCounter.get(name) || 0;\n if (count === maxCount) {\n throw new Error(error);\n }\n instanceCounter.set(name, count + 1);\n\n return () => {\n const currentCount = instanceCounter.get(name) || 0;\n instanceCounter.set(name, Math.max(0, currentCount - 1));\n };\n }, []);\n}\n\n/**\n * A higher-order component that wraps a component with instance count protection.\n * This HOC ensures that the wrapped component cannot be instantiated more than once\n * (or a specified number of times) in the application.\n *\n * @param WrappedComponent - The component to wrap with instance count protection\n * @param name - A unique identifier for the component type\n * @param error - The error message to display if the maximum count is exceeded\n * @returns A new component with instance count protection\n *\n * @example\n * ```tsx\n * const ProtectedComponent = withMaxAllowedInstancesGuard(\n * MyComponent,\n * 'MyComponent',\n * 'Only one instance of MyComponent is allowed'\n * );\n * ```\n */\nexport function withMaxAllowedInstancesGuard<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n name: string,\n error: string\n): React.ComponentType<P> {\n const displayName =\n WrappedComponent.displayName ||\n WrappedComponent.name ||\n name ||\n 'Component';\n\n const Hoc: React.FC<P> = (props) => {\n useMaxAllowedInstancesGuard(name, error);\n return <WrappedComponent {...props} />;\n };\n\n Hoc.displayName = `withMaxAllowedInstancesGuard(${displayName})`;\n return Hoc;\n}\n","export const DOMAIN = 'mcl.spur.us';\n"],"mappings":"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","names":["import_react","React","import_jsx_runtime","assessment"]}
package/dist/index.mjs CHANGED
@@ -79,28 +79,9 @@ var MonocleProviderComponent = ({
79
79
  try {
80
80
  setIsLoading(true);
81
81
  setError(null);
82
- await loadScript();
83
82
  if (window.MCL) {
84
- let timeoutId = null;
85
- await window.MCL.configure({
86
- onAssessment: (assessment2) => {
87
- if (timeoutId) {
88
- clearTimeout(timeoutId);
89
- }
90
- setAssessment(assessment2);
91
- setIsLoading(false);
92
- }
93
- });
94
- const existingAssessment = window.MCL.getAssessment();
95
- if (existingAssessment) {
96
- setAssessment(existingAssessment);
97
- setIsLoading(false);
98
- } else {
99
- timeoutId = setTimeout(() => {
100
- setError(new Error("Assessment timeout - MCL did not respond within 30 seconds"));
101
- setIsLoading(false);
102
- }, 3e4);
103
- }
83
+ await window.MCL.refresh();
84
+ setIsLoading(false);
104
85
  } else {
105
86
  throw new Error("MCL object not found on window");
106
87
  }
@@ -110,17 +91,40 @@ var MonocleProviderComponent = ({
110
91
  );
111
92
  setIsLoading(false);
112
93
  }
113
- }, [publishableKey, domain]);
94
+ }, []);
114
95
  useEffect(() => {
96
+ const initializeMCL = async () => {
97
+ try {
98
+ await loadScript();
99
+ if (window.MCL) {
100
+ await window.MCL.configure({
101
+ onAssessment: (assessment2) => {
102
+ setAssessment(assessment2);
103
+ setIsLoading(false);
104
+ }
105
+ });
106
+ const existingAssessment = window.MCL.getAssessment();
107
+ if (existingAssessment) {
108
+ setAssessment(existingAssessment);
109
+ setIsLoading(false);
110
+ }
111
+ }
112
+ } catch (err) {
113
+ setError(
114
+ err instanceof Error ? err : new Error("Failed to initialize MCL")
115
+ );
116
+ setIsLoading(false);
117
+ }
118
+ };
115
119
  if (!assessment) {
116
- refresh();
120
+ initializeMCL();
117
121
  }
118
122
  return () => {
119
123
  if (window.MCL) {
120
124
  window.MCL.configure({ onAssessment: void 0 });
121
125
  }
122
126
  };
123
- }, [publishableKey, domain, assessment, refresh]);
127
+ }, [publishableKey, domain]);
124
128
  const contextValue = useMemo(
125
129
  () => ({ assessment, refresh, isLoading, error }),
126
130
  [assessment, refresh, isLoading, error]
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/contexts/MonocleProvider.tsx","../src/utils/useMaxAllowedInstancesGuard.tsx","../src/constants.ts"],"sourcesContent":["import React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useMemo,\n useCallback,\n} from 'react';\nimport { withMaxAllowedInstancesGuard } from '../utils';\nimport { MonocleProviderProps } from '../types';\nimport { DOMAIN } from '../constants';\ninterface MonocleContextType {\n assessment: string | undefined;\n refresh: () => void;\n isLoading: boolean;\n error: Error | null;\n}\n\nconst MonocleContext = createContext<MonocleContextType | null>(null);\n\nconst MonocleProviderComponent: React.FC<MonocleProviderProps> = ({\n children,\n publishableKey,\n domain = DOMAIN,\n}) => {\n const [assessment, setAssessment] = useState<string | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const loadScript = () => {\n return new Promise<void>((resolve, reject) => {\n const existingScript = document.getElementById('_mcl');\n if (existingScript) {\n // If script exists but hasn't loaded yet, wait for it\n if (!window.MCL) {\n existingScript.onload = () => resolve();\n existingScript.onerror = () =>\n reject(new Error('Failed to load Monocle script'));\n } else {\n resolve();\n }\n return;\n }\n\n const script = document.createElement('script');\n script.id = '_mcl';\n script.async = true;\n script.src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;\n script.onload = () => {\n resolve();\n };\n script.onerror = (_e) => {\n console.error('MonocleProvider: Script failed to load');\n reject(new Error('Failed to load Monocle script'));\n };\n document.head.appendChild(script);\n });\n };\n\n const refresh = useCallback(async () => {\n try {\n setIsLoading(true);\n setError(null);\n await loadScript();\n if (window.MCL) {\n let timeoutId: NodeJS.Timeout | null = null;\n \n // Configure MCL with our callback to receive assessment updates\n await window.MCL.configure({\n onAssessment: (assessment: string) => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n setAssessment(assessment);\n setIsLoading(false);\n },\n });\n\n // Check if assessment is already available\n const existingAssessment = window.MCL.getAssessment();\n if (existingAssessment) {\n setAssessment(existingAssessment);\n setIsLoading(false);\n } else {\n // Set a timeout in case the assessment never comes\n timeoutId = setTimeout(() => {\n setError(new Error('Assessment timeout - MCL did not respond within 30 seconds'));\n setIsLoading(false);\n }, 30000);\n }\n } else {\n throw new Error('MCL object not found on window');\n }\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error('Unknown error occurred')\n );\n setIsLoading(false);\n }\n }, [publishableKey, domain]);\n\n useEffect(() => {\n // Only refresh if the publishableKey changes and we don't already have an assessment\n if (!assessment) {\n refresh();\n }\n \n // Cleanup function to reset callback on unmount\n return () => {\n if (window.MCL) {\n window.MCL.configure({ onAssessment: undefined });\n }\n };\n }, [publishableKey, domain, assessment, refresh]);\n\n const contextValue = useMemo(\n () => ({ assessment, refresh, isLoading, error }),\n [assessment, refresh, isLoading, error]\n );\n\n return (\n <MonocleContext.Provider value={contextValue}>\n {children}\n </MonocleContext.Provider>\n );\n};\n\nexport const MonocleProvider = withMaxAllowedInstancesGuard(\n MonocleProviderComponent,\n 'MonocleProvider',\n 'Only one instance of MonocleProvider is allowed'\n);\n\n/**\n * Hook to access the Monocle context.\n *\n * @returns {MonocleContextType} The Monocle context containing assessment data, loading state, and error information\n * @throws {Error} When used outside of a MonocleProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { assessment, isLoading, error, refresh } = useMonocle();\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return <div>Assessment: {assessment}</div>;\n * }\n * ```\n */\nexport const useMonocle = () => {\n const context = useContext(MonocleContext);\n if (!context) {\n throw new Error('useMonocle must be used within a MonocleProvider');\n }\n return context;\n};\n","import React from 'react';\n\nconst instanceCounter = new Map<string, number>();\n\n/**\n * A React hook that ensures a component is not instantiated more than a specified number of times.\n * Throws an error if the maximum number of instances is exceeded.\n *\n * @param name - A unique identifier for the component type\n * @param error - The error message to display if the maximum count is exceeded\n * @param maxCount - The maximum number of allowed instances (defaults to 1)\n * @throws Error when the maximum number of instances is exceeded\n *\n * @example\n * ```tsx\n * const MyComponent = () => {\n * useMaxAllowedInstancesGuard('MyComponent', 'Only one instance of MyComponent is allowed');\n * return <div>My Component</div>;\n * };\n * ```\n */\nexport function useMaxAllowedInstancesGuard(\n name: string,\n error: string,\n maxCount = 1\n): void {\n React.useEffect(() => {\n const count = instanceCounter.get(name) || 0;\n if (count === maxCount) {\n throw new Error(error);\n }\n instanceCounter.set(name, count + 1);\n\n return () => {\n const currentCount = instanceCounter.get(name) || 0;\n instanceCounter.set(name, Math.max(0, currentCount - 1));\n };\n }, []);\n}\n\n/**\n * A higher-order component that wraps a component with instance count protection.\n * This HOC ensures that the wrapped component cannot be instantiated more than once\n * (or a specified number of times) in the application.\n *\n * @param WrappedComponent - The component to wrap with instance count protection\n * @param name - A unique identifier for the component type\n * @param error - The error message to display if the maximum count is exceeded\n * @returns A new component with instance count protection\n *\n * @example\n * ```tsx\n * const ProtectedComponent = withMaxAllowedInstancesGuard(\n * MyComponent,\n * 'MyComponent',\n * 'Only one instance of MyComponent is allowed'\n * );\n * ```\n */\nexport function withMaxAllowedInstancesGuard<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n name: string,\n error: string\n): React.ComponentType<P> {\n const displayName =\n WrappedComponent.displayName ||\n WrappedComponent.name ||\n name ||\n 'Component';\n\n const Hoc: React.FC<P> = (props) => {\n useMaxAllowedInstancesGuard(name, error);\n return <WrappedComponent {...props} />;\n };\n\n Hoc.displayName = `withMaxAllowedInstancesGuard(${displayName})`;\n return Hoc;\n}\n","export const DOMAIN = 'mcl.spur.us';\n"],"mappings":"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","names":["jsx","assessment"]}
1
+ {"version":3,"sources":["../src/contexts/MonocleProvider.tsx","../src/utils/useMaxAllowedInstancesGuard.tsx","../src/constants.ts"],"sourcesContent":["import React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useMemo,\n useCallback,\n} from 'react';\nimport { withMaxAllowedInstancesGuard } from '../utils';\nimport { MonocleProviderProps } from '../types';\nimport { DOMAIN } from '../constants';\ninterface MonocleContextType {\n assessment: string | undefined;\n refresh: () => void;\n isLoading: boolean;\n error: Error | null;\n}\n\nconst MonocleContext = createContext<MonocleContextType | null>(null);\n\nconst MonocleProviderComponent: React.FC<MonocleProviderProps> = ({\n children,\n publishableKey,\n domain = DOMAIN,\n}) => {\n const [assessment, setAssessment] = useState<string | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n const loadScript = () => {\n return new Promise<void>((resolve, reject) => {\n const existingScript = document.getElementById('_mcl');\n if (existingScript) {\n // If script exists but hasn't loaded yet, wait for it\n if (!window.MCL) {\n existingScript.onload = () => resolve();\n existingScript.onerror = () =>\n reject(new Error('Failed to load Monocle script'));\n } else {\n resolve();\n }\n return;\n }\n\n const script = document.createElement('script');\n script.id = '_mcl';\n script.async = true;\n script.src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;\n script.onload = () => {\n resolve();\n };\n script.onerror = (_e) => {\n console.error('MonocleProvider: Script failed to load');\n reject(new Error('Failed to load Monocle script'));\n };\n document.head.appendChild(script);\n });\n };\n\n const refresh = useCallback(async () => {\n try {\n setIsLoading(true);\n setError(null);\n \n if (window.MCL) {\n await window.MCL.refresh();\n setIsLoading(false);\n } else {\n throw new Error('MCL object not found on window');\n }\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error('Unknown error occurred')\n );\n setIsLoading(false);\n }\n }, []);\n\n useEffect(() => {\n const initializeMCL = async () => {\n try {\n await loadScript();\n if (window.MCL) {\n // Configure MCL with our callback to receive assessment updates\n await window.MCL.configure({\n onAssessment: (assessment: string) => {\n setAssessment(assessment);\n setIsLoading(false);\n },\n });\n\n // Check if assessment is already available\n const existingAssessment = window.MCL.getAssessment();\n if (existingAssessment) {\n setAssessment(existingAssessment);\n setIsLoading(false);\n }\n }\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error('Failed to initialize MCL')\n );\n setIsLoading(false);\n }\n };\n\n // Only initialize if we don't already have an assessment\n if (!assessment) {\n initializeMCL();\n }\n \n // Cleanup function to reset callback on unmount\n return () => {\n if (window.MCL) {\n window.MCL.configure({ onAssessment: undefined });\n }\n };\n }, [publishableKey, domain]);\n\n const contextValue = useMemo(\n () => ({ assessment, refresh, isLoading, error }),\n [assessment, refresh, isLoading, error]\n );\n\n return (\n <MonocleContext.Provider value={contextValue}>\n {children}\n </MonocleContext.Provider>\n );\n};\n\nexport const MonocleProvider = withMaxAllowedInstancesGuard(\n MonocleProviderComponent,\n 'MonocleProvider',\n 'Only one instance of MonocleProvider is allowed'\n);\n\n/**\n * Hook to access the Monocle context.\n *\n * @returns {MonocleContextType} The Monocle context containing assessment data, loading state, and error information\n * @throws {Error} When used outside of a MonocleProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { assessment, isLoading, error, refresh } = useMonocle();\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return <div>Assessment: {assessment}</div>;\n * }\n * ```\n */\nexport const useMonocle = () => {\n const context = useContext(MonocleContext);\n if (!context) {\n throw new Error('useMonocle must be used within a MonocleProvider');\n }\n return context;\n};\n","import React from 'react';\n\nconst instanceCounter = new Map<string, number>();\n\n/**\n * A React hook that ensures a component is not instantiated more than a specified number of times.\n * Throws an error if the maximum number of instances is exceeded.\n *\n * @param name - A unique identifier for the component type\n * @param error - The error message to display if the maximum count is exceeded\n * @param maxCount - The maximum number of allowed instances (defaults to 1)\n * @throws Error when the maximum number of instances is exceeded\n *\n * @example\n * ```tsx\n * const MyComponent = () => {\n * useMaxAllowedInstancesGuard('MyComponent', 'Only one instance of MyComponent is allowed');\n * return <div>My Component</div>;\n * };\n * ```\n */\nexport function useMaxAllowedInstancesGuard(\n name: string,\n error: string,\n maxCount = 1\n): void {\n React.useEffect(() => {\n const count = instanceCounter.get(name) || 0;\n if (count === maxCount) {\n throw new Error(error);\n }\n instanceCounter.set(name, count + 1);\n\n return () => {\n const currentCount = instanceCounter.get(name) || 0;\n instanceCounter.set(name, Math.max(0, currentCount - 1));\n };\n }, []);\n}\n\n/**\n * A higher-order component that wraps a component with instance count protection.\n * This HOC ensures that the wrapped component cannot be instantiated more than once\n * (or a specified number of times) in the application.\n *\n * @param WrappedComponent - The component to wrap with instance count protection\n * @param name - A unique identifier for the component type\n * @param error - The error message to display if the maximum count is exceeded\n * @returns A new component with instance count protection\n *\n * @example\n * ```tsx\n * const ProtectedComponent = withMaxAllowedInstancesGuard(\n * MyComponent,\n * 'MyComponent',\n * 'Only one instance of MyComponent is allowed'\n * );\n * ```\n */\nexport function withMaxAllowedInstancesGuard<P extends object>(\n WrappedComponent: React.ComponentType<P>,\n name: string,\n error: string\n): React.ComponentType<P> {\n const displayName =\n WrappedComponent.displayName ||\n WrappedComponent.name ||\n name ||\n 'Component';\n\n const Hoc: React.FC<P> = (props) => {\n useMaxAllowedInstancesGuard(name, error);\n return <WrappedComponent {...props} />;\n };\n\n Hoc.displayName = `withMaxAllowedInstancesGuard(${displayName})`;\n return Hoc;\n}\n","export const DOMAIN = 'mcl.spur.us';\n"],"mappings":"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","names":["jsx","assessment"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spur.us/monocle-react",
3
- "version": "1.1.1-canary.v20250707133917",
3
+ "version": "1.1.1-canary.v20250918205642",
4
4
  "description": "Monocle React library",
5
5
  "keywords": [
6
6
  "spur",
@@ -20,7 +20,7 @@
20
20
  "author": "Spur",
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
- "@spur.us/types": "^0.3.0-canary.v20250707133917"
23
+ "@spur.us/types": "^0.3.0-canary.v20250918205642"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0",
@@ -61,33 +61,10 @@ const MonocleProviderComponent: React.FC<MonocleProviderProps> = ({
61
61
  try {
62
62
  setIsLoading(true);
63
63
  setError(null);
64
- await loadScript();
64
+
65
65
  if (window.MCL) {
66
- let timeoutId: NodeJS.Timeout | null = null;
67
-
68
- // Configure MCL with our callback to receive assessment updates
69
- await window.MCL.configure({
70
- onAssessment: (assessment: string) => {
71
- if (timeoutId) {
72
- clearTimeout(timeoutId);
73
- }
74
- setAssessment(assessment);
75
- setIsLoading(false);
76
- },
77
- });
78
-
79
- // Check if assessment is already available
80
- const existingAssessment = window.MCL.getAssessment();
81
- if (existingAssessment) {
82
- setAssessment(existingAssessment);
83
- setIsLoading(false);
84
- } else {
85
- // Set a timeout in case the assessment never comes
86
- timeoutId = setTimeout(() => {
87
- setError(new Error('Assessment timeout - MCL did not respond within 30 seconds'));
88
- setIsLoading(false);
89
- }, 30000);
90
- }
66
+ await window.MCL.refresh();
67
+ setIsLoading(false);
91
68
  } else {
92
69
  throw new Error('MCL object not found on window');
93
70
  }
@@ -97,12 +74,39 @@ const MonocleProviderComponent: React.FC<MonocleProviderProps> = ({
97
74
  );
98
75
  setIsLoading(false);
99
76
  }
100
- }, [publishableKey, domain]);
77
+ }, []);
101
78
 
102
79
  useEffect(() => {
103
- // Only refresh if the publishableKey changes and we don't already have an assessment
80
+ const initializeMCL = async () => {
81
+ try {
82
+ await loadScript();
83
+ if (window.MCL) {
84
+ // Configure MCL with our callback to receive assessment updates
85
+ await window.MCL.configure({
86
+ onAssessment: (assessment: string) => {
87
+ setAssessment(assessment);
88
+ setIsLoading(false);
89
+ },
90
+ });
91
+
92
+ // Check if assessment is already available
93
+ const existingAssessment = window.MCL.getAssessment();
94
+ if (existingAssessment) {
95
+ setAssessment(existingAssessment);
96
+ setIsLoading(false);
97
+ }
98
+ }
99
+ } catch (err) {
100
+ setError(
101
+ err instanceof Error ? err : new Error('Failed to initialize MCL')
102
+ );
103
+ setIsLoading(false);
104
+ }
105
+ };
106
+
107
+ // Only initialize if we don't already have an assessment
104
108
  if (!assessment) {
105
- refresh();
109
+ initializeMCL();
106
110
  }
107
111
 
108
112
  // Cleanup function to reset callback on unmount
@@ -111,7 +115,7 @@ const MonocleProviderComponent: React.FC<MonocleProviderProps> = ({
111
115
  window.MCL.configure({ onAssessment: undefined });
112
116
  }
113
117
  };
114
- }, [publishableKey, domain, assessment, refresh]);
118
+ }, [publishableKey, domain]);
115
119
 
116
120
  const contextValue = useMemo(
117
121
  () => ({ assessment, refresh, isLoading, error }),