@sitecore-marketplace-sdk/client 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +210 -53
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -3,16 +3,17 @@
3
3
  The `client` package provides secure, bidirectional communication between a Marketplace application (the client) and Sitecore (the host). Sitecore loads the Marketplace app inside a sandboxed [iframe](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe). The iframe and its parent window securely communicate using the web browser's [PostMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage).
4
4
 
5
5
  This package lets you:
6
- - Make queries. Queries support one-off data requests and subscriptions for live updates. The `client` package lets you query the host's state and environment, and the [context](#query-the-application-context) of your Marketplace app.
7
- - Perform mutations. Mutations trigger state changes or HTTP requests in Sitecore.
8
- - Interact with Sitecore APIs to perform actions on behalf of the resources it was granted access to during installation.
9
6
 
10
- > [!TIP]
11
- > Inspired by GraphQL and React Query, the query/mutation API manages internal state, loading status, and error handling.
7
+ - Make queries. Queries support one-off data requests and subscriptions for live updates. The `client` package lets you query the host's state and environment, and the [context](#query-the-application-context) of your Marketplace app.
8
+ - Make mutations. Mutations trigger state changes or HTTP requests in Sitecore.
9
+ - Interact with Sitecore APIs to perform actions on behalf of the resource access it was granted during installation.
10
+ > [!TIP]
11
+ > Inspired by GraphQL and React Query, the query/mutation API manages internal state, loading status, and error handling.
12
12
 
13
13
  The `client` package is required for all Marketplace apps.
14
14
 
15
15
  ## Prerequisites
16
+
16
17
  - Node.js 16 or later. Check your installed version by using the `node --version` command.
17
18
  - npm 10 or later. Check your installed version by using the `npm --version` command.
18
19
  - Access to the Sitecore Cloud Portal.
@@ -25,66 +26,210 @@ npm install @sitecore-marketplace-sdk/client
25
26
 
26
27
  ## Initialization
27
28
 
28
- Before you use queries or mutations, you must initialize the Client SDK:
29
+ Before you use queries or mutations, you must initialize the Client SDK.
30
+
31
+ 1. Create a hook that will handle the initialization:
29
32
 
30
33
  ```typescript
34
+ // utils/hooks/useMarketplaceClient.ts
35
+
31
36
  import { ClientSDK } from '@sitecore-marketplace-sdk/client';
37
+ import { useEffect, useState, useCallback, useMemo, useRef } from 'react';
32
38
 
33
- // Create a configuration object:
34
- const config = {
35
- origin: 'https://pages.sitecorecloud.io', // Where in Sitecore to display the app
36
- target: window.parent, // To iframe the app
37
- modules: [] // Extend Client SDK with other modules, such as `XMC`
39
+ export interface MarketplaceClientState {
40
+ client: ClientSDK | null;
41
+ error: Error | null;
42
+ isLoading: boolean;
43
+ isInitialized: boolean;
44
+ }
45
+
46
+ export interface UseMarketplaceClientOptions {
47
+ /**
48
+ * Number of retry attempts when initialization fails
49
+ * @default 3
50
+ */
51
+ retryAttempts?: number;
52
+
53
+ /**
54
+ * Delay between retry attempts in milliseconds
55
+ * @default 1000
56
+ */
57
+ retryDelay?: number;
58
+
59
+ /**
60
+ * Whether to automatically initialize the client
61
+ * @default true
62
+ */
63
+ autoInit?: boolean;
64
+ }
65
+
66
+ const DEFAULT_OPTIONS: Required<UseMarketplaceClientOptions> = {
67
+ retryAttempts: 3,
68
+ retryDelay: 1000,
69
+ autoInit: true,
38
70
  };
39
71
 
40
- // Create a Client SDK instance using the configuration. The returned SDK provides a type-safe API based on your resource schema:
41
- const client = await ClientSDK.init(config);
42
- ```
72
+ let client: ClientSDK | undefined = undefined;
43
73
 
44
- ## Usage
74
+ async function getMarketplaceClient() {
75
+ if (client) {
76
+ return client;
77
+ }
45
78
 
46
- ### Make a query
79
+ const config = {
80
+ target: window.parent,
81
+ };
47
82
 
48
- Use the `query` method to make one-off data requests and live subscriptions. Pass a value to the method depending on the data you want to retrieve.
83
+ client = await ClientSDK.init(config);
84
+ return client;
85
+ }
49
86
 
50
- For example, pass `'host.state'` to retrieve the status of the host application:
87
+ export function useMarketplaceClient(options: UseMarketplaceClientOptions = {}) {
88
+ // Memoize the options to prevent unnecessary re-renders
89
+ const opts = useMemo(() => ({ ...DEFAULT_OPTIONS, ...options }), [options]);
90
+
91
+ const [state, setState] = useState<MarketplaceClientState>({
92
+ client: null,
93
+ error: null,
94
+ isLoading: false,
95
+ isInitialized: false,
96
+ });
97
+
98
+ // Use ref to track if we're currently initializing to prevent race conditions
99
+ const isInitializingRef = useRef(false);
100
+
101
+ const initializeClient = useCallback(
102
+ async (attempt = 1): Promise<void> => {
103
+ // Use functional state update to check current state without dependencies
104
+ let shouldProceed = false;
105
+ setState((prev) => {
106
+ if (prev.isLoading || prev.isInitialized || isInitializingRef.current) {
107
+ return prev;
108
+ }
109
+ shouldProceed = true;
110
+ isInitializingRef.current = true;
111
+ return { ...prev, isLoading: true, error: null };
112
+ });
113
+
114
+ if (!shouldProceed) return;
115
+
116
+ try {
117
+ const client = await getMarketplaceClient();
118
+ setState({
119
+ client,
120
+ error: null,
121
+ isLoading: false,
122
+ isInitialized: true,
123
+ });
124
+ } catch (error) {
125
+ if (attempt < opts.retryAttempts) {
126
+ await new Promise((resolve) => setTimeout(resolve, opts.retryDelay));
127
+ return initializeClient(attempt + 1);
128
+ }
129
+
130
+ setState({
131
+ client: null,
132
+ error:
133
+ error instanceof Error ? error : new Error('Failed to initialize MarketplaceClient'),
134
+ isLoading: false,
135
+ isInitialized: false,
136
+ });
137
+ } finally {
138
+ isInitializingRef.current = false;
139
+ }
140
+ },
141
+ [opts.retryAttempts, opts.retryDelay],
142
+ ); // Removed state dependencies
143
+
144
+ useEffect(() => {
145
+ if (opts.autoInit) {
146
+ initializeClient();
147
+ }
148
+
149
+ return () => {
150
+ isInitializingRef.current = false;
151
+ setState({
152
+ client: null,
153
+ error: null,
154
+ isLoading: false,
155
+ isInitialized: false,
156
+ });
157
+ };
158
+ }, [opts.autoInit, initializeClient]);
159
+
160
+ // Memoize the return value to prevent object recreation on every render
161
+ return useMemo(
162
+ () => ({
163
+ ...state,
164
+ initialize: initializeClient,
165
+ }),
166
+ [state, initializeClient],
167
+ );
168
+ }
169
+ ```
170
+
171
+ 2. Initialize the SDK:
51
172
 
52
173
  ```typescript
53
- (async () => {
54
- // One-off query example: Request host state once.
55
- const queryResult = await client.query('host.state');
174
+ // src/pages/index.tsx
175
+
176
+ import { useState, useEffect } from "react";
177
+ import type { ApplicationContext } from "@sitecore-marketplace-sdk/client";
178
+ import { useMarketplaceClient } from "@/utils/hooks/useMarketplaceClient";
179
+
180
+ function App() {
181
+ const { client, error, isInitialized } = useMarketplaceClient();
182
+ const [appContext, setAppContext] = useState<ApplicationContext>();
183
+
184
+ useEffect(() => {
185
+ if (!error && isInitialized && client) {
186
+ console.log("Marketplace client initialized successfully.");
187
+
188
+ // Make a query to retrieve the application context
189
+ client.query("application.context")
190
+ .then((res) => {
191
+ console.log("Success retrieving application.context:", res.data);
192
+ setAppContext(res.data);
193
+ })
194
+ .catch((error) => {
195
+ console.error("Error retrieving application.context:", error);
196
+ });
197
+ } else if (error) {
198
+ console.error("Error initializing Marketplace client:", error);
199
+ }
200
+ }, [client, error, isInitialized]);
201
+
202
+ return (
203
+ <>
204
+ <h1>Welcome to {appContext?.name}</h1>
205
+ </>
206
+ );
207
+ }
56
208
 
57
- console.log(queryResult.data); // Displays the host state data
58
- console.log(queryResult.isLoading); // false once the request is complete
209
+ export default App;
59
210
  ```
60
211
 
61
- For an overview of all the possible values, refer to the [`QueryMap` interface](../../docs/client/interfaces/QueryMap.md).
212
+ ## Usage
62
213
 
63
- ### Perform a mutation
214
+ ### Make a query
64
215
 
65
- Use the `mutate` method to trigger changes in Sitecore (the host). Pass a value to the method depending on the change you want to make.
216
+ Use the `query` method to make one-off data requests and live subscriptions. Pass a value to the method depending on the data you want to retrieve.
66
217
 
67
- For example, pass `'pages.reloadCanvas'` to reload the XM Cloud page builder canvas:
218
+ For example, pass `'application.context'` to retrieve details about the Marketplace app and the Sitecore host, including, for example, your Context IDs:
68
219
 
69
220
  ```typescript
70
- (async () => {
71
- try {
72
- const mutationResponse = await client.mutate('pages.reloadCanvas');
73
- console.log('Mutation response:', mutationResponse);
74
- } catch (error) {
75
- console.error('Error during mutation:', error);
76
- }
77
- })();
221
+ client
222
+ .query('application.context')
223
+ .then((res) => {
224
+ console.log('Success retrieving application.context:', res.data);
225
+ setAppContext(res.data);
226
+ })
227
+ .catch((error) => {
228
+ console.error('Error retrieving application.context:', error);
229
+ });
78
230
  ```
79
231
 
80
- For an overview of all the possible values, refer to the [`MutationMap` interface](../../docs/client/interfaces/MutationMap.md).
81
-
82
- > [!NOTE]
83
- > Behind the scenes, the Host SDK (integrated via the internal `core` package) attaches the required user token and performs the HTTP request on behalf of the Marketplace app (the client).
84
-
85
- ### Query the application context
86
-
87
- The application context provides information about your Marketplace app, such as its ID, URL, name, type, icon URL, installation ID, and associated resources:
232
+ The application context provides information about your Marketplace app, such as its ID, URL, name, type, icon URL, installation ID, and associated resource access:
88
233
 
89
234
  ```javascript
90
235
  {
@@ -94,13 +239,13 @@ The application context provides information about your Marketplace app, such as
94
239
  url: 'https://my-app.com/app',
95
240
  iconUrl: 'https://my-app.com/assets/icon.png',
96
241
  installationId: 'abc1234567890',
97
- resources: [
242
+ resourceAccess: [
98
243
  {
99
244
  resourceId: 'resource-1',
100
245
  tenantId: 'tenant-1',
101
246
  tenantName: 'Example Tenant',
102
247
  context: {
103
- live: '1234567890',
248
+ live: '1234567890',
104
249
  preview: '0987654321'
105
250
  }
106
251
  }
@@ -108,21 +253,33 @@ The application context provides information about your Marketplace app, such as
108
253
  }
109
254
  ```
110
255
 
111
- To retrieve the application context, use the `query` method and pass `'application.context'` to it:
256
+ For an overview of all the possible values, refer to the [`QueryMap` interface](../../docs/client/interfaces/QueryMap.md).
257
+
258
+ ### Make a mutation
259
+
260
+ Use the `mutate` method to trigger changes in Sitecore (the host). Pass a value to the method depending on the change you want to make.
261
+
262
+ For example, pass `'pages.reloadCanvas'` to reload the XM Cloud page builder canvas:
112
263
 
113
264
  ```typescript
114
- (async () => {
115
- const queryResult = await client.query('application.context');
116
- console.log(queryResult.data); // Displays the application context data
117
- })();
265
+ const reloadCanvas = () => {
266
+ client?.mutate('pages.reloadCanvas');
267
+ };
118
268
  ```
119
269
 
270
+ For an overview of all the possible values, refer to the [`MutationMap` interface](../../docs/client/interfaces/MutationMap.md).
271
+
272
+ > [!NOTE]
273
+ > Behind the scenes, the Host SDK (integrated via the internal `core` package) attaches the required user token and performs the HTTP request on behalf of the Marketplace app (the client).
274
+
120
275
  ## Documentation
121
276
 
122
- For more information, refer to the reference documentation in the `/docs` folder.
277
+ For more information, refer to the reference documentation in the `/docs` folder, and the official [Marketplace developer documentation](https://doc.sitecore.com/mp/en/developers/marketplace/introduction-to-sitecore-marketplace.html) and [Marketplace SDK documentation](https://doc.sitecore.com/mp/en/developers/sdk/latest/sitecore-marketplace-sdk/sitecore-marketplace-sdk-for-javascript.html).
123
278
 
124
- ## License
125
- This package is part of the Sitecore Marketplace SDK, licensed under the Apache 2.0 License. Refer to the [LICENSE](../../LICENSE.MD) file in the repository root.
279
+ ## License
280
+
281
+ This package is part of the Sitecore Marketplace SDK, licensed under the Apache 2.0 License. Refer to the [LICENSE](../../LICENSE.md) file in the repository root.
126
282
 
127
283
  ## Status
284
+
128
285
  The `client` package is actively maintained as part of the Sitecore Marketplace SDK.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sitecore-marketplace-sdk/client",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs.js",
@@ -29,7 +29,7 @@
29
29
  "dependencies": {
30
30
  "tslib": "^2.8.1",
31
31
  "uuid": "^11.1.0",
32
- "@sitecore-marketplace-sdk/core": "0.1.4"
32
+ "@sitecore-marketplace-sdk/core": "0.2.1"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@rollup/plugin-node-resolve": "^16.0.0",
@@ -69,6 +69,6 @@
69
69
  "lint": "eslint src --ext .ts",
70
70
  "test": "vitest",
71
71
  "coverage": "vitest run --coverage",
72
- "generate:docs": "npx typedoc --plugin typedoc-plugin-markdown --outputFileStrategy Members --parametersFormat table --readme none --out ../../docs/client src/index.ts --githubPages false"
72
+ "generate:docs": "npx typedoc --options ../../typedoc.json --out ../../docs/client src/index.ts"
73
73
  }
74
74
  }