@spur.us/monocle-react 1.2.2-canary.v20260122213010 → 1.2.2-canary.v20260123183735
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.
- package/.turbo/turbo-build.log +8 -8
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/contexts/MonocleProvider.tsx +3 -4
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @spur.us/monocle-react@1.2.2-canary.
|
|
2
|
+
> @spur.us/monocle-react@1.2.2-canary.v20260123183735 build /home/runner/work/javascript/javascript/packages/monocle-react
|
|
3
3
|
> tsup
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: {"index":"src/index.ts"}
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mCJS[39m Build start
|
|
12
12
|
[34mESM[39m Build start
|
|
13
|
-
[32mESM[39m [1mdist/index.mjs [22m[32m4.
|
|
14
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[32m10.
|
|
15
|
-
[32mESM[39m ⚡️ Build success in
|
|
16
|
-
[32mCJS[39m [1mdist/index.js [22m[32m6.
|
|
17
|
-
[32mCJS[39m [1mdist/index.js.map [22m[32m10.
|
|
18
|
-
[32mCJS[39m ⚡️ Build success in
|
|
13
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m4.69 KB[39m
|
|
14
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m10.66 KB[39m
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 32ms
|
|
16
|
+
[32mCJS[39m [1mdist/index.js [22m[32m6.49 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m10.81 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 32ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 2394ms
|
|
21
21
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.55 KB[39m
|
|
22
22
|
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.55 KB[39m
|
package/dist/index.js
CHANGED
|
@@ -82,10 +82,9 @@ var MonocleProviderComponent = ({
|
|
|
82
82
|
const [error, setError] = (0, import_react2.useState)(null);
|
|
83
83
|
const loadScript = () => {
|
|
84
84
|
return new Promise((resolve, reject) => {
|
|
85
|
-
const createScriptError = (e
|
|
85
|
+
const createScriptError = (e) => {
|
|
86
86
|
const errorDetails = [
|
|
87
|
-
"Failed to load Monocle script"
|
|
88
|
-
`URL: ${scriptUrl}`
|
|
87
|
+
"Failed to load Monocle script"
|
|
89
88
|
];
|
|
90
89
|
if (e instanceof ErrorEvent && e.message) {
|
|
91
90
|
errorDetails.push(`Message: ${e.message}`);
|
|
@@ -101,7 +100,7 @@ var MonocleProviderComponent = ({
|
|
|
101
100
|
if (existingScript) {
|
|
102
101
|
if (!window.MCL) {
|
|
103
102
|
existingScript.onload = () => resolve();
|
|
104
|
-
existingScript.onerror = (e) => reject(createScriptError(e
|
|
103
|
+
existingScript.onerror = (e) => reject(createScriptError(e));
|
|
105
104
|
} else {
|
|
106
105
|
resolve();
|
|
107
106
|
}
|
|
@@ -119,7 +118,7 @@ var MonocleProviderComponent = ({
|
|
|
119
118
|
resolve();
|
|
120
119
|
};
|
|
121
120
|
script.onerror = (e) => {
|
|
122
|
-
reject(createScriptError(e
|
|
121
|
+
reject(createScriptError(e));
|
|
123
122
|
};
|
|
124
123
|
document.head.appendChild(script);
|
|
125
124
|
});
|
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';\n\ninterface MonocleContextType {\n assessment: string | undefined;\n refresh: () => Promise<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 cpd,\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 createScriptError = (e: Event | string, scriptUrl: string) => {\n const errorDetails = [\n 'Failed to load Monocle script',\n `URL: ${scriptUrl}`,\n ];\n\n if (e instanceof ErrorEvent && e.message) {\n errorDetails.push(`Message: ${e.message}`);\n }\n\n if (!navigator.onLine) {\n errorDetails.push('Device appears to be offline');\n }\n\n const error = new Error(errorDetails.join('. '));\n console.error('MonocleProvider:', error);\n return error;\n };\n\n const existingScript = document.getElementById('_mcl') as HTMLScriptElement;\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 = (e) =>\n reject(createScriptError(e, existingScript.src));\n } else {\n resolve();\n }\n return;\n }\n\n const script = document.createElement('script');\n script.id = '_mcl';\n script.async = true;\n\n let src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;\n if (cpd) {\n src += `&cpd=${cpd}`;\n }\n script.src = src;\n\n script.onload = () => {\n resolve();\n };\n script.onerror = (e) => {\n reject(createScriptError(e, script.src));\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 } 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","error","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';\n\ninterface MonocleContextType {\n assessment: string | undefined;\n refresh: () => Promise<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 cpd,\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 createScriptError = (e: Event | string) => {\n const errorDetails = [\n 'Failed to load Monocle script',\n ];\n\n if (e instanceof ErrorEvent && e.message) {\n errorDetails.push(`Message: ${e.message}`);\n }\n\n if (!navigator.onLine) {\n errorDetails.push('Device appears to be offline');\n }\n\n const error = new Error(errorDetails.join('. '));\n console.error('MonocleProvider:', error);\n return error;\n };\n\n const existingScript = document.getElementById('_mcl') as HTMLScriptElement;\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 = (e) =>\n reject(createScriptError(e));\n } else {\n resolve();\n }\n return;\n }\n\n const script = document.createElement('script');\n script.id = '_mcl';\n script.async = true;\n\n let src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;\n if (cpd) {\n src += `&cpd=${cpd}`;\n }\n script.src = src;\n\n script.onload = () => {\n resolve();\n };\n script.onerror = (e) => {\n reject(createScriptError(e));\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 } 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","error","assessment"]}
|
package/dist/index.mjs
CHANGED
|
@@ -52,10 +52,9 @@ var MonocleProviderComponent = ({
|
|
|
52
52
|
const [error, setError] = useState(null);
|
|
53
53
|
const loadScript = () => {
|
|
54
54
|
return new Promise((resolve, reject) => {
|
|
55
|
-
const createScriptError = (e
|
|
55
|
+
const createScriptError = (e) => {
|
|
56
56
|
const errorDetails = [
|
|
57
|
-
"Failed to load Monocle script"
|
|
58
|
-
`URL: ${scriptUrl}`
|
|
57
|
+
"Failed to load Monocle script"
|
|
59
58
|
];
|
|
60
59
|
if (e instanceof ErrorEvent && e.message) {
|
|
61
60
|
errorDetails.push(`Message: ${e.message}`);
|
|
@@ -71,7 +70,7 @@ var MonocleProviderComponent = ({
|
|
|
71
70
|
if (existingScript) {
|
|
72
71
|
if (!window.MCL) {
|
|
73
72
|
existingScript.onload = () => resolve();
|
|
74
|
-
existingScript.onerror = (e) => reject(createScriptError(e
|
|
73
|
+
existingScript.onerror = (e) => reject(createScriptError(e));
|
|
75
74
|
} else {
|
|
76
75
|
resolve();
|
|
77
76
|
}
|
|
@@ -89,7 +88,7 @@ var MonocleProviderComponent = ({
|
|
|
89
88
|
resolve();
|
|
90
89
|
};
|
|
91
90
|
script.onerror = (e) => {
|
|
92
|
-
reject(createScriptError(e
|
|
91
|
+
reject(createScriptError(e));
|
|
93
92
|
};
|
|
94
93
|
document.head.appendChild(script);
|
|
95
94
|
});
|
package/dist/index.mjs.map
CHANGED
|
@@ -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';\n\ninterface MonocleContextType {\n assessment: string | undefined;\n refresh: () => Promise<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 cpd,\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 createScriptError = (e: Event | string, scriptUrl: string) => {\n const errorDetails = [\n 'Failed to load Monocle script',\n `URL: ${scriptUrl}`,\n ];\n\n if (e instanceof ErrorEvent && e.message) {\n errorDetails.push(`Message: ${e.message}`);\n }\n\n if (!navigator.onLine) {\n errorDetails.push('Device appears to be offline');\n }\n\n const error = new Error(errorDetails.join('. '));\n console.error('MonocleProvider:', error);\n return error;\n };\n\n const existingScript = document.getElementById('_mcl') as HTMLScriptElement;\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 = (e) =>\n reject(createScriptError(e, existingScript.src));\n } else {\n resolve();\n }\n return;\n }\n\n const script = document.createElement('script');\n script.id = '_mcl';\n script.async = true;\n\n let src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;\n if (cpd) {\n src += `&cpd=${cpd}`;\n }\n script.src = src;\n\n script.onload = () => {\n resolve();\n };\n script.onerror = (e) => {\n reject(createScriptError(e, script.src));\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 } 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","error","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';\n\ninterface MonocleContextType {\n assessment: string | undefined;\n refresh: () => Promise<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 cpd,\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 createScriptError = (e: Event | string) => {\n const errorDetails = [\n 'Failed to load Monocle script',\n ];\n\n if (e instanceof ErrorEvent && e.message) {\n errorDetails.push(`Message: ${e.message}`);\n }\n\n if (!navigator.onLine) {\n errorDetails.push('Device appears to be offline');\n }\n\n const error = new Error(errorDetails.join('. '));\n console.error('MonocleProvider:', error);\n return error;\n };\n\n const existingScript = document.getElementById('_mcl') as HTMLScriptElement;\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 = (e) =>\n reject(createScriptError(e));\n } else {\n resolve();\n }\n return;\n }\n\n const script = document.createElement('script');\n script.id = '_mcl';\n script.async = true;\n\n let src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;\n if (cpd) {\n src += `&cpd=${cpd}`;\n }\n script.src = src;\n\n script.onload = () => {\n resolve();\n };\n script.onerror = (e) => {\n reject(createScriptError(e));\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 } 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","error","assessment"]}
|
package/package.json
CHANGED
|
@@ -31,10 +31,9 @@ const MonocleProviderComponent: React.FC<MonocleProviderProps> = ({
|
|
|
31
31
|
|
|
32
32
|
const loadScript = () => {
|
|
33
33
|
return new Promise<void>((resolve, reject) => {
|
|
34
|
-
const createScriptError = (e: Event | string
|
|
34
|
+
const createScriptError = (e: Event | string) => {
|
|
35
35
|
const errorDetails = [
|
|
36
36
|
'Failed to load Monocle script',
|
|
37
|
-
`URL: ${scriptUrl}`,
|
|
38
37
|
];
|
|
39
38
|
|
|
40
39
|
if (e instanceof ErrorEvent && e.message) {
|
|
@@ -56,7 +55,7 @@ const MonocleProviderComponent: React.FC<MonocleProviderProps> = ({
|
|
|
56
55
|
if (!window.MCL) {
|
|
57
56
|
existingScript.onload = () => resolve();
|
|
58
57
|
existingScript.onerror = (e) =>
|
|
59
|
-
reject(createScriptError(e
|
|
58
|
+
reject(createScriptError(e));
|
|
60
59
|
} else {
|
|
61
60
|
resolve();
|
|
62
61
|
}
|
|
@@ -77,7 +76,7 @@ const MonocleProviderComponent: React.FC<MonocleProviderProps> = ({
|
|
|
77
76
|
resolve();
|
|
78
77
|
};
|
|
79
78
|
script.onerror = (e) => {
|
|
80
|
-
reject(createScriptError(e
|
|
79
|
+
reject(createScriptError(e));
|
|
81
80
|
};
|
|
82
81
|
document.head.appendChild(script);
|
|
83
82
|
});
|