@sitecore-marketplace-sdk/client 0.1.4 → 0.2.2
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/README.md +210 -53
- package/package.json +4 -4
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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
41
|
-
const client = await ClientSDK.init(config);
|
|
42
|
-
```
|
|
72
|
+
let client: ClientSDK | undefined = undefined;
|
|
43
73
|
|
|
44
|
-
|
|
74
|
+
async function getMarketplaceClient() {
|
|
75
|
+
if (client) {
|
|
76
|
+
return client;
|
|
77
|
+
}
|
|
45
78
|
|
|
46
|
-
|
|
79
|
+
const config = {
|
|
80
|
+
target: window.parent,
|
|
81
|
+
};
|
|
47
82
|
|
|
48
|
-
|
|
83
|
+
client = await ClientSDK.init(config);
|
|
84
|
+
return client;
|
|
85
|
+
}
|
|
49
86
|
|
|
50
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
58
|
-
console.log(queryResult.isLoading); // false once the request is complete
|
|
209
|
+
export default App;
|
|
59
210
|
```
|
|
60
211
|
|
|
61
|
-
|
|
212
|
+
## Usage
|
|
62
213
|
|
|
63
|
-
###
|
|
214
|
+
### Make a query
|
|
64
215
|
|
|
65
|
-
Use the `
|
|
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 `'
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
console.log('
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs.js",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"client"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
+
"@sitecore-marketplace-sdk/core": "0.2.2",
|
|
30
31
|
"tslib": "^2.8.1",
|
|
31
|
-
"uuid": "^11.1.0"
|
|
32
|
-
"@sitecore-marketplace-sdk/core": "0.1.4"
|
|
32
|
+
"uuid": "^11.1.0"
|
|
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 --
|
|
72
|
+
"generate:docs": "npx typedoc --options ../../typedoc.json --out ../../docs/client src/index.ts"
|
|
73
73
|
}
|
|
74
74
|
}
|