@spur.us/monocle-react 0.1.0 → 1.1.0-canary.v20250520214223
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 +10 -10
- package/CHANGELOG.md +13 -0
- package/README.md +4 -4
- package/dist/index.d.mts +17 -8
- package/dist/index.d.ts +17 -8
- package/dist/index.js +11 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/constants.ts +1 -0
- package/src/contexts/MonocleProvider.tsx +11 -16
- package/src/index.ts +2 -0
- package/src/types.ts +12 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @spur.us/monocle-react@
|
|
2
|
+
> @spur.us/monocle-react@1.1.0-canary.v20250520214223 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[32m3.
|
|
14
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[32m7.
|
|
15
|
-
[32mESM[39m ⚡️ Build success in
|
|
16
|
-
[32mCJS[39m [1mdist/index.js [22m[32m5.
|
|
17
|
-
[32mCJS[39m [1mdist/index.js.map [22m[32m7.
|
|
18
|
-
[32mCJS[39m ⚡️ Build success in
|
|
13
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m3.35 KB[39m
|
|
14
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m7.54 KB[39m
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 43ms
|
|
16
|
+
[32mCJS[39m [1mdist/index.js [22m[32m5.15 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m7.72 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 44ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 2322ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m806.00 B[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m806.00 B[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @spur.us/monocle-react
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- a2b3919: - Rename `bundle` to `assessment` in `useMonocle` hook
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- d377c8a: Remove 'use client' directive.
|
|
12
|
+
- Updated dependencies [a2b3919]
|
|
13
|
+
- Updated dependencies [d7071af]
|
|
14
|
+
- @spur.us/types@0.2.0
|
|
15
|
+
|
|
3
16
|
## 0.1.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<a href="https://
|
|
2
|
+
<a href="https://spur.us">
|
|
3
3
|
<picture>
|
|
4
4
|
<source media="(prefers-color-scheme: dark)" srcset="../../docs/images/logo-dark-mode.svg">
|
|
5
5
|
<img alt="Spur logo" src="../../docs/images/logo-light-mode.svg" height="128">
|
|
@@ -15,11 +15,11 @@ A React library for integrating Monocle into your React applications. This packa
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npm install @spur/monocle-react
|
|
18
|
+
npm install @spur.us/monocle-react
|
|
19
19
|
# or
|
|
20
|
-
yarn add @spur/monocle-react
|
|
20
|
+
yarn add @spur.us/monocle-react
|
|
21
21
|
# or
|
|
22
|
-
pnpm add @spur/monocle-react
|
|
22
|
+
pnpm add @spur.us/monocle-react
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Contributing
|
package/dist/index.d.mts
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React$1 from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Props for the MonocleProvider component.
|
|
5
|
+
* @interface MonocleProviderProps
|
|
6
|
+
*/
|
|
7
|
+
interface MonocleProviderProps {
|
|
8
|
+
/** The child components to be wrapped by the MonocleProvider. */
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
/** The publishable key used for authentication with Monocle. */
|
|
11
|
+
publishableKey: string;
|
|
12
|
+
/** Optional base domain for the Monocle API. Defaults to `mcl.spur.us` if not provided */
|
|
13
|
+
domain?: string;
|
|
14
|
+
}
|
|
2
15
|
|
|
3
16
|
interface MonocleContextType {
|
|
4
|
-
|
|
17
|
+
assessment: string | undefined;
|
|
5
18
|
refresh: () => void;
|
|
6
19
|
isLoading: boolean;
|
|
7
20
|
error: Error | null;
|
|
8
21
|
}
|
|
9
|
-
|
|
10
|
-
children: React.ReactNode;
|
|
11
|
-
publishableKey: string;
|
|
12
|
-
}
|
|
13
|
-
declare const MonocleProvider: React.ComponentType<MonocleProviderProps>;
|
|
22
|
+
declare const MonocleProvider: React$1.ComponentType<MonocleProviderProps>;
|
|
14
23
|
declare const useMonocle: () => MonocleContextType;
|
|
15
24
|
|
|
16
|
-
export { MonocleProvider, useMonocle };
|
|
25
|
+
export { MonocleProvider, type MonocleProviderProps, useMonocle };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React$1 from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Props for the MonocleProvider component.
|
|
5
|
+
* @interface MonocleProviderProps
|
|
6
|
+
*/
|
|
7
|
+
interface MonocleProviderProps {
|
|
8
|
+
/** The child components to be wrapped by the MonocleProvider. */
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
/** The publishable key used for authentication with Monocle. */
|
|
11
|
+
publishableKey: string;
|
|
12
|
+
/** Optional base domain for the Monocle API. Defaults to `mcl.spur.us` if not provided */
|
|
13
|
+
domain?: string;
|
|
14
|
+
}
|
|
2
15
|
|
|
3
16
|
interface MonocleContextType {
|
|
4
|
-
|
|
17
|
+
assessment: string | undefined;
|
|
5
18
|
refresh: () => void;
|
|
6
19
|
isLoading: boolean;
|
|
7
20
|
error: Error | null;
|
|
8
21
|
}
|
|
9
|
-
|
|
10
|
-
children: React.ReactNode;
|
|
11
|
-
publishableKey: string;
|
|
12
|
-
}
|
|
13
|
-
declare const MonocleProvider: React.ComponentType<MonocleProviderProps>;
|
|
22
|
+
declare const MonocleProvider: React$1.ComponentType<MonocleProviderProps>;
|
|
14
23
|
declare const useMonocle: () => MonocleContextType;
|
|
15
24
|
|
|
16
|
-
export { MonocleProvider, useMonocle };
|
|
25
|
+
export { MonocleProvider, type MonocleProviderProps, useMonocle };
|
package/dist/index.js
CHANGED
|
@@ -64,14 +64,18 @@ function withMaxAllowedInstancesGuard(WrappedComponent, name, error) {
|
|
|
64
64
|
return Hoc;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// src/constants.ts
|
|
68
|
+
var DOMAIN = "mcl.spur.us";
|
|
69
|
+
|
|
67
70
|
// src/contexts/MonocleProvider.tsx
|
|
68
71
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
69
72
|
var MonocleContext = (0, import_react2.createContext)(null);
|
|
70
73
|
var MonocleProviderComponent = ({
|
|
71
74
|
children,
|
|
72
|
-
publishableKey
|
|
75
|
+
publishableKey,
|
|
76
|
+
domain = DOMAIN
|
|
73
77
|
}) => {
|
|
74
|
-
const [
|
|
78
|
+
const [assessment, setAssessment] = (0, import_react2.useState)(void 0);
|
|
75
79
|
const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
|
|
76
80
|
const [error, setError] = (0, import_react2.useState)(null);
|
|
77
81
|
const loadScript = () => {
|
|
@@ -89,7 +93,7 @@ var MonocleProviderComponent = ({
|
|
|
89
93
|
const script = document.createElement("script");
|
|
90
94
|
script.id = "_mcl";
|
|
91
95
|
script.async = true;
|
|
92
|
-
script.src = `https
|
|
96
|
+
script.src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;
|
|
93
97
|
script.onload = () => {
|
|
94
98
|
resolve();
|
|
95
99
|
};
|
|
@@ -106,8 +110,8 @@ var MonocleProviderComponent = ({
|
|
|
106
110
|
setError(null);
|
|
107
111
|
await loadScript();
|
|
108
112
|
if (window.MCL) {
|
|
109
|
-
const
|
|
110
|
-
|
|
113
|
+
const newAssessment = window.MCL.getAssessment();
|
|
114
|
+
setAssessment(newAssessment);
|
|
111
115
|
} else {
|
|
112
116
|
throw new Error("MCL object not found on window");
|
|
113
117
|
}
|
|
@@ -120,11 +124,11 @@ var MonocleProviderComponent = ({
|
|
|
120
124
|
}
|
|
121
125
|
};
|
|
122
126
|
(0, import_react2.useEffect)(() => {
|
|
123
|
-
if (!
|
|
127
|
+
if (!assessment) {
|
|
124
128
|
refresh();
|
|
125
129
|
}
|
|
126
130
|
}, [publishableKey]);
|
|
127
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MonocleContext.Provider, { value: {
|
|
131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MonocleContext.Provider, { value: { assessment, refresh, isLoading, error }, children });
|
|
128
132
|
};
|
|
129
133
|
var MonocleProvider = withMaxAllowedInstancesGuard(
|
|
130
134
|
MonocleProviderComponent,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/contexts/MonocleProvider.tsx","../src/utils/useMaxAllowedInstancesGuard.tsx"],"sourcesContent":["export * from './contexts';\n
|
|
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, { createContext, useContext, useEffect, useState } 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 = async () => {\n try {\n setIsLoading(true);\n setError(null);\n await loadScript();\n if (window.MCL) {\n const newAssessment = window.MCL.getAssessment();\n setAssessment(newAssessment);\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 } finally {\n setIsLoading(false);\n }\n };\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 }, [publishableKey]);\n\n return (\n <MonocleContext.Provider value={{ assessment, refresh, isLoading, error }}>\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\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 instanceCounter.set(name, (instanceCounter.get(name) || 1) - 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"]}
|
package/dist/index.mjs
CHANGED
|
@@ -27,14 +27,18 @@ function withMaxAllowedInstancesGuard(WrappedComponent, name, error) {
|
|
|
27
27
|
return Hoc;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// src/constants.ts
|
|
31
|
+
var DOMAIN = "mcl.spur.us";
|
|
32
|
+
|
|
30
33
|
// src/contexts/MonocleProvider.tsx
|
|
31
34
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
32
35
|
var MonocleContext = createContext(null);
|
|
33
36
|
var MonocleProviderComponent = ({
|
|
34
37
|
children,
|
|
35
|
-
publishableKey
|
|
38
|
+
publishableKey,
|
|
39
|
+
domain = DOMAIN
|
|
36
40
|
}) => {
|
|
37
|
-
const [
|
|
41
|
+
const [assessment, setAssessment] = useState(void 0);
|
|
38
42
|
const [isLoading, setIsLoading] = useState(true);
|
|
39
43
|
const [error, setError] = useState(null);
|
|
40
44
|
const loadScript = () => {
|
|
@@ -52,7 +56,7 @@ var MonocleProviderComponent = ({
|
|
|
52
56
|
const script = document.createElement("script");
|
|
53
57
|
script.id = "_mcl";
|
|
54
58
|
script.async = true;
|
|
55
|
-
script.src = `https
|
|
59
|
+
script.src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;
|
|
56
60
|
script.onload = () => {
|
|
57
61
|
resolve();
|
|
58
62
|
};
|
|
@@ -69,8 +73,8 @@ var MonocleProviderComponent = ({
|
|
|
69
73
|
setError(null);
|
|
70
74
|
await loadScript();
|
|
71
75
|
if (window.MCL) {
|
|
72
|
-
const
|
|
73
|
-
|
|
76
|
+
const newAssessment = window.MCL.getAssessment();
|
|
77
|
+
setAssessment(newAssessment);
|
|
74
78
|
} else {
|
|
75
79
|
throw new Error("MCL object not found on window");
|
|
76
80
|
}
|
|
@@ -83,11 +87,11 @@ var MonocleProviderComponent = ({
|
|
|
83
87
|
}
|
|
84
88
|
};
|
|
85
89
|
useEffect(() => {
|
|
86
|
-
if (!
|
|
90
|
+
if (!assessment) {
|
|
87
91
|
refresh();
|
|
88
92
|
}
|
|
89
93
|
}, [publishableKey]);
|
|
90
|
-
return /* @__PURE__ */ jsx2(MonocleContext.Provider, { value: {
|
|
94
|
+
return /* @__PURE__ */ jsx2(MonocleContext.Provider, { value: { assessment, refresh, isLoading, error }, children });
|
|
91
95
|
};
|
|
92
96
|
var MonocleProvider = withMaxAllowedInstancesGuard(
|
|
93
97
|
MonocleProviderComponent,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/contexts/MonocleProvider.tsx","../src/utils/useMaxAllowedInstancesGuard.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../src/contexts/MonocleProvider.tsx","../src/utils/useMaxAllowedInstancesGuard.tsx","../src/constants.ts"],"sourcesContent":["import React, { createContext, useContext, useEffect, useState } 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 = async () => {\n try {\n setIsLoading(true);\n setError(null);\n await loadScript();\n if (window.MCL) {\n const newAssessment = window.MCL.getAssessment();\n setAssessment(newAssessment);\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 } finally {\n setIsLoading(false);\n }\n };\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 }, [publishableKey]);\n\n return (\n <MonocleContext.Provider value={{ assessment, refresh, isLoading, error }}>\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\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 instanceCounter.set(name, (instanceCounter.get(name) || 1) - 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":";AAAA,SAAgB,eAAe,YAAY,WAAW,gBAAgB;;;ACAtE,OAAO,WAAW;AAuEP;AArEX,IAAM,kBAAkB,oBAAI,IAAoB;AAmBzC,SAAS,4BACd,MACA,OACA,WAAW,GACL;AACN,QAAM,UAAU,MAAM;AACpB,UAAM,QAAQ,gBAAgB,IAAI,IAAI,KAAK;AAC3C,QAAI,UAAU,UAAU;AACtB,YAAM,IAAI,MAAM,KAAK;AAAA,IACvB;AACA,oBAAgB,IAAI,MAAM,QAAQ,CAAC;AAEnC,WAAO,MAAM;AACX,sBAAgB,IAAI,OAAO,gBAAgB,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,IAChE;AAAA,EACF,GAAG,CAAC,CAAC;AACP;AAqBO,SAAS,6BACd,kBACA,MACA,OACwB;AACxB,QAAM,cACJ,iBAAiB,eACjB,iBAAiB,QACjB,QACA;AAEF,QAAM,MAAmB,CAAC,UAAU;AAClC,gCAA4B,MAAM,KAAK;AACvC,WAAO,oBAAC,oBAAkB,GAAG,OAAO;AAAA,EACtC;AAEA,MAAI,cAAc,gCAAgC,WAAW;AAC7D,SAAO;AACT;;;AC5EO,IAAM,SAAS;;;AFgFlB,gBAAAA,YAAA;AArEJ,IAAM,iBAAiB,cAAyC,IAAI;AAEpE,IAAM,2BAA2D,CAAC;AAAA,EAChE;AAAA,EACA;AAAA,EACA,SAAS;AACX,MAAM;AACJ,QAAM,CAAC,YAAY,aAAa,IAAI,SAA6B,MAAS;AAC1E,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,IAAI;AAC/C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AAErD,QAAM,aAAa,MAAM;AACvB,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,YAAM,iBAAiB,SAAS,eAAe,MAAM;AACrD,UAAI,gBAAgB;AAElB,YAAI,CAAC,OAAO,KAAK;AACf,yBAAe,SAAS,MAAM,QAAQ;AACtC,yBAAe,UAAU,MACvB,OAAO,IAAI,MAAM,+BAA+B,CAAC;AAAA,QACrD,OAAO;AACL,kBAAQ;AAAA,QACV;AACA;AAAA,MACF;AAEA,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,KAAK;AACZ,aAAO,QAAQ;AACf,aAAO,MAAM,WAAW,MAAM,gBAAgB,cAAc;AAC5D,aAAO,SAAS,MAAM;AACpB,gBAAQ;AAAA,MACV;AACA,aAAO,UAAU,CAAC,OAAO;AACvB,gBAAQ,MAAM,wCAAwC;AACtD,eAAO,IAAI,MAAM,+BAA+B,CAAC;AAAA,MACnD;AACA,eAAS,KAAK,YAAY,MAAM;AAAA,IAClC,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,YAAY;AAC1B,QAAI;AACF,mBAAa,IAAI;AACjB,eAAS,IAAI;AACb,YAAM,WAAW;AACjB,UAAI,OAAO,KAAK;AACd,cAAM,gBAAgB,OAAO,IAAI,cAAc;AAC/C,sBAAc,aAAa;AAAA,MAC7B,OAAO;AACL,cAAM,IAAI,MAAM,gCAAgC;AAAA,MAClD;AAAA,IACF,SAAS,KAAK;AACZ;AAAA,QACE,eAAe,QAAQ,MAAM,IAAI,MAAM,wBAAwB;AAAA,MACjE;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,YAAU,MAAM;AAEd,QAAI,CAAC,YAAY;AACf,cAAQ;AAAA,IACV;AAAA,EACF,GAAG,CAAC,cAAc,CAAC;AAEnB,SACE,gBAAAA,KAAC,eAAe,UAAf,EAAwB,OAAO,EAAE,YAAY,SAAS,WAAW,MAAM,GACrE,UACH;AAEJ;AAEO,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF;AAEO,IAAM,aAAa,MAAM;AAC9B,QAAM,UAAU,WAAW,cAAc;AACzC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AACA,SAAO;AACT;","names":["jsx"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spur.us/monocle-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0-canary.v20250520214223",
|
|
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.
|
|
23
|
+
"@spur.us/types": "^0.2.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-0",
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DOMAIN = 'mcl.spur.us';
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
1
|
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
4
2
|
import { withMaxAllowedInstancesGuard } from '../utils';
|
|
5
|
-
|
|
3
|
+
import { MonocleProviderProps } from '../types';
|
|
4
|
+
import { DOMAIN } from '../constants';
|
|
6
5
|
interface MonocleContextType {
|
|
7
|
-
|
|
6
|
+
assessment: string | undefined;
|
|
8
7
|
refresh: () => void;
|
|
9
8
|
isLoading: boolean;
|
|
10
9
|
error: Error | null;
|
|
@@ -12,16 +11,12 @@ interface MonocleContextType {
|
|
|
12
11
|
|
|
13
12
|
const MonocleContext = createContext<MonocleContextType | null>(null);
|
|
14
13
|
|
|
15
|
-
interface MonocleProviderProps {
|
|
16
|
-
children: React.ReactNode;
|
|
17
|
-
publishableKey: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
14
|
const MonocleProviderComponent: React.FC<MonocleProviderProps> = ({
|
|
21
15
|
children,
|
|
22
16
|
publishableKey,
|
|
17
|
+
domain = DOMAIN,
|
|
23
18
|
}) => {
|
|
24
|
-
const [
|
|
19
|
+
const [assessment, setAssessment] = useState<string | undefined>(undefined);
|
|
25
20
|
const [isLoading, setIsLoading] = useState(true);
|
|
26
21
|
const [error, setError] = useState<Error | null>(null);
|
|
27
22
|
|
|
@@ -43,7 +38,7 @@ const MonocleProviderComponent: React.FC<MonocleProviderProps> = ({
|
|
|
43
38
|
const script = document.createElement('script');
|
|
44
39
|
script.id = '_mcl';
|
|
45
40
|
script.async = true;
|
|
46
|
-
script.src = `https
|
|
41
|
+
script.src = `https://${domain}/d/mcl.js?tk=${publishableKey}`;
|
|
47
42
|
script.onload = () => {
|
|
48
43
|
resolve();
|
|
49
44
|
};
|
|
@@ -61,8 +56,8 @@ const MonocleProviderComponent: React.FC<MonocleProviderProps> = ({
|
|
|
61
56
|
setError(null);
|
|
62
57
|
await loadScript();
|
|
63
58
|
if (window.MCL) {
|
|
64
|
-
const
|
|
65
|
-
|
|
59
|
+
const newAssessment = window.MCL.getAssessment();
|
|
60
|
+
setAssessment(newAssessment);
|
|
66
61
|
} else {
|
|
67
62
|
throw new Error('MCL object not found on window');
|
|
68
63
|
}
|
|
@@ -76,14 +71,14 @@ const MonocleProviderComponent: React.FC<MonocleProviderProps> = ({
|
|
|
76
71
|
};
|
|
77
72
|
|
|
78
73
|
useEffect(() => {
|
|
79
|
-
// Only refresh if the publishableKey changes and we don't already have
|
|
80
|
-
if (!
|
|
74
|
+
// Only refresh if the publishableKey changes and we don't already have an assessment
|
|
75
|
+
if (!assessment) {
|
|
81
76
|
refresh();
|
|
82
77
|
}
|
|
83
78
|
}, [publishableKey]);
|
|
84
79
|
|
|
85
80
|
return (
|
|
86
|
-
<MonocleContext.Provider value={{
|
|
81
|
+
<MonocleContext.Provider value={{ assessment, refresh, isLoading, error }}>
|
|
87
82
|
{children}
|
|
88
83
|
</MonocleContext.Provider>
|
|
89
84
|
);
|
package/src/index.ts
CHANGED
package/src/types.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Props for the MonocleProvider component.
|
|
3
|
+
* @interface MonocleProviderProps
|
|
4
|
+
*/
|
|
5
|
+
export interface MonocleProviderProps {
|
|
6
|
+
/** The child components to be wrapped by the MonocleProvider. */
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
/** The publishable key used for authentication with Monocle. */
|
|
9
|
+
publishableKey: string;
|
|
10
|
+
/** Optional base domain for the Monocle API. Defaults to `mcl.spur.us` if not provided */
|
|
11
|
+
domain?: string;
|
|
12
|
+
}
|