@croct/plug-react 0.3.0 → 0.4.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.
@@ -1,6 +1,6 @@
1
- import { FunctionComponent } from 'react';
1
+ import { FunctionComponent, PropsWithChildren } from 'react';
2
2
  import { Configuration, Plug } from '@croct/plug';
3
- export declare type CroctProviderProps = Configuration & Required<Pick<Configuration, 'appId'>>;
3
+ export declare type CroctProviderProps = PropsWithChildren<Configuration & Required<Pick<Configuration, 'appId'>>>;
4
4
  export declare const CroctContext: import("react").Context<{
5
5
  plug: Plug;
6
6
  } | null>;
package/README.md CHANGED
@@ -33,16 +33,16 @@ The React Plug library provides components and hooks for personalizing applicati
33
33
  - **Blazing-fast queries**: double-digit millisecond latency for real-time evaluations.
34
34
  - **Playground integration**: one-click to connect, no configuration needed.
35
35
 
36
- Check out the [Storybook](https://croct-tech.github.io/plug-react) to see some minimal examples in action,
36
+ Check out the [Storybook](https://croct-tech.github.io/plug-react) to see some minimal examples in action,
37
37
  or the [example folder](examples) for full code examples.
38
38
 
39
39
  ## Getting Started
40
40
 
41
41
  The following steps will walk you through installing the library and integrating it into your application.
42
42
 
43
- This guide assumes you're already familiar with some key concepts and tools around Croct, like
44
- Contextual Query Language (CQL) and the playground. If you're not,
45
- [this 15-minute quickstart](https://croct.link/plug-js/quick-start) that will give you a hands-on overview of
43
+ This guide assumes you're already familiar with some key concepts and tools around Croct, like
44
+ Contextual Query Language (CQL) and the playground. If you're not,
45
+ [this 15-minute quickstart](https://croct.link/plug-js/quick-start) that will give you a hands-on overview of
46
46
  all the terms and tools you need to get started.
47
47
 
48
48
  ### Installation
@@ -57,10 +57,10 @@ npm install @croct/plug-react
57
57
 
58
58
  ### Plugging in
59
59
 
60
- You connect Croct to React with the `<CroctProvider/ >` component. The `<CroctProvider/ >` uses a regular React's
60
+ You connect Croct to React with the `<CroctProvider />` component. The `<CroctProvider />` uses a regular React's
61
61
  `<Context.Provider />` to wrap your React app and make the SDK available anywhere in your component tree.
62
62
 
63
- We suggest putting the `<CroctProvider/ >` somewhere high in your app, above any component that might be personalized,
63
+ We suggest putting the `<CroctProvider />` somewhere high in your app, above any component that might be personalized,
64
64
  ideally in the top-level `<App/>` component.
65
65
 
66
66
  ```tsx
@@ -69,7 +69,7 @@ import {CroctProvider} from '@croct/plug-react';
69
69
 
70
70
  function App() {
71
71
  return (
72
- <CroctProvider appId="00000000-0000-0000-0000-000000000000">
72
+ <CroctProvider appId="<APP_ID>">
73
73
  <div>
74
74
  <h1>My first personalized app 🚀</h1>
75
75
  </div>
@@ -80,6 +80,9 @@ function App() {
80
80
  render(<App />, document.getElementById('root'));
81
81
  ```
82
82
 
83
+ > Replace `<APP_ID>` with your public app ID that you can find at Workspaces > Applications > Setup.
84
+ > In case you don't have an account yet, you can use the sandbox application ID `00000000-0000-0000-0000-000000000000`
85
+
83
86
  ### Evaluating expressions
84
87
 
85
88
  Once your application is plugged in, you're ready to start personalizing your components using the `<Personalization />`
@@ -90,7 +93,7 @@ to conditionally renders a different button depending on the user's persona.
90
93
 
91
94
  ![Evaluation Example](https://user-images.githubusercontent.com/943036/116588852-6a114200-a8f2-11eb-9d88-c346f002e2a1.png)
92
95
 
93
- Let's first implement the use-case using the `<Personalization/>` component. It takes an expression (e.g. `user's persona`)
96
+ Let's first implement the use-case using the `<Personalization />` component. It takes an expression (e.g. `user's persona`)
94
97
  and a render function, which tells the component how to render the UI, depending on the evaluation result.
95
98
 
96
99
  This is what our component would look like:
@@ -156,15 +159,16 @@ export default function OnboardingPage(): ReactElement {
156
159
  }
157
160
  ```
158
161
 
159
- If you run the application and there is no persona assigned to your profile, you will see the button for non-developers
162
+ If you run the application and there is no persona assigned to your profile, you will see the button for non-developers
160
163
  — otherwise, the button for sharing the code with developers.
161
- Check out [Accessing the Plug instance](#accessing-the-plug-instance) for an example of how to save information in a
164
+ Check out [Accessing the Plug instance](#accessing-the-plug-instance) for an example of how to save information in a
162
165
  user's profile.
163
166
 
164
167
  #### Fault tolerance
165
168
 
166
- We strongly recommend always specifying the `fallback` property to ensure your app behaves the same way regardless of
167
- the personalization. In this way, the UI will still be fully functional even in maintenance windows.
169
+ We strongly recommend specifying the `fallback` property in client-side rendered applications to ensure your app behaves
170
+ the same way regardless of the personalization. In this way, the UI will still be fully functional even in maintenance
171
+ windows. **Specifying a `fallback` is required for server-side rendering (SSR).**
168
172
 
169
173
  The following example shows how you can specify a fallback behaviour for the docs link:
170
174
 
@@ -182,7 +186,7 @@ export default function OnboardingPage(): ReactElement {
182
186
  return (
183
187
  <Suspense fallback="✨ Personalizing...">
184
188
  {/* Using the <Personalization /> component */}
185
- <Personalization expression="user's persona is 'developer'" falback={false}>
189
+ <Personalization expression="user's persona is 'developer'" fallback={false}>
186
190
  {(isDeveloper: boolean) => (
187
191
  <Fragment>
188
192
  {isDeveloper && <a href="/docs">View docs</a>}
@@ -201,23 +205,23 @@ For a full list of the available options, please refer to the [API documentation
201
205
 
202
206
  ### Using slots
203
207
 
204
- Evaluating expression is a flexible and powerful way to customize your UI. However, for components whose content
205
- changes too often, this approach can be overkill. For those cases, we encourage you to use the Slots feature instead.
206
- Using slots gives your team the flexibility to change the content or personalization rules whenever needed without
208
+ Evaluating expression is a flexible and powerful way to customize your UI. However, for components whose content
209
+ changes too often, this approach can be overkill. For those cases, we encourage you to use the Slots feature instead.
210
+ Using slots gives your team the flexibility to change the content or personalization rules whenever needed without
207
211
  touching the component code.
208
212
 
209
213
  ![Slot Example](https://user-images.githubusercontent.com/943036/116586841-44833900-a8f0-11eb-8d32-acec2eacee01.png)
210
214
 
211
- To render a slot, all you need to do is provide the `id` you configured in your Croct workspace. Based on the
212
- slot's personalization rules and the user's context, the component will decide which content show to that user.
213
- Notice that there's no logic on the client-side, meaning that your marketing or product team can freely change the
215
+ To render a slot, all you need to do is provide the `id` you configured in your Croct workspace. Based on the
216
+ slot's personalization rules and the user's context, the component will decide which content show to that user.
217
+ Notice that there's no logic on the client-side, meaning that your marketing or product team can freely change the
214
218
  slot content as they need without requiring an update to your React app.
215
219
 
216
- For the next example, we assume that you have already defined a slot with id `home-banner` in your Croct workspace
220
+ For the next example, we assume that you have already defined a slot with id `home-banner` in your Croct workspace
217
221
  with the following structure:
218
222
 
219
223
  ```ts
220
- type HomeBanner = {
224
+ type HomeBannerContent = {
221
225
  title: string,
222
226
  subtitle: string,
223
227
  cta: {
@@ -235,11 +239,20 @@ Here's how to use the `<Slot />` component:
235
239
  import {ReactElement, Suspense} from 'react';
236
240
  import {Slot} from '@croct/plug-react';
237
241
 
242
+ type HomeBannerContent = {
243
+ title: string,
244
+ subtitle: string,
245
+ cta: {
246
+ label: string,
247
+ link: string,
248
+ },
249
+ };
250
+
238
251
  export default function OnboardingPage(): ReactElement {
239
252
  return (
240
253
  <Suspense fallback="✨ Personalizing content...">
241
254
  <Slot id="home-banner">
242
- {({title, subtitle, cta}: HomeBanner) => (
255
+ {({title, subtitle, cta}: HomeBannerContent) => (
243
256
  <div>
244
257
  <strong>{title}</strong>
245
258
  <p>{subtitle}</p>
@@ -258,17 +271,26 @@ To avoid the component from suspending while loading, you can provide an `initia
258
271
  import {ReactElement} from 'react';
259
272
  import {Slot} from '@croct/plug-react';
260
273
 
274
+ type HomeBannerContent = {
275
+ title: string,
276
+ subtitle: string,
277
+ cta: {
278
+ label: string,
279
+ link: string,
280
+ },
281
+ };
282
+
261
283
  export default function OnboardingPage(): ReactElement {
262
284
  return (
263
285
  <Slot id="home-banner" initial={null}>
264
- {(props: HomeBanner|null) => (
286
+ {(props: HomeBannerContent|null) => (
265
287
  props === null
266
288
  ? '✨ Personalizing...'
267
289
  : (
268
290
  <div>
269
- <strong>{title}</strong>
270
- <p>{subtitle}</p>
271
- <a href={cta.link}>{cta.label}</a>
291
+ <strong>{props.title}</strong>
292
+ <p>{props.subtitle}</p>
293
+ <a href={props.cta.link}>{props.cta.label}</a>
272
294
  </div>
273
295
  )
274
296
  )}
@@ -283,8 +305,17 @@ And here's an example using the `useContent` hook:
283
305
  import {ReactElement, Suspense} from 'react';
284
306
  import {useContent} from '@croct/plug-react';
285
307
 
308
+ type HomeBannerContent = {
309
+ title: string,
310
+ subtitle: string,
311
+ cta: {
312
+ label: string,
313
+ link: string,
314
+ },
315
+ };
316
+
286
317
  function HomeBanner(): ReactElement {
287
- const banner = useContent<HomeBanner>('home-banner');
318
+ const {title, subtitle, cta} = useContent<HomeBannerContent>('home-banner');
288
319
 
289
320
  return (
290
321
  <div>
@@ -312,7 +343,16 @@ The following example shows how you can specify a fallback state for the `home-b
312
343
  import {ReactElement, Suspense} from 'react';
313
344
  import {Slot, useContent} from '@croct/plug-react';
314
345
 
315
- const fallbackBanner: HomeBanner = {
346
+ type HomeBannerContent = {
347
+ title: string,
348
+ subtitle: string,
349
+ cta: {
350
+ label: string,
351
+ link: string,
352
+ },
353
+ };
354
+
355
+ const fallbackBanner: HomeBannerContent = {
316
356
  title: 'Default title',
317
357
  subtitle: 'Default subtitle',
318
358
  cta: {
@@ -322,7 +362,7 @@ const fallbackBanner: HomeBanner = {
322
362
  };
323
363
 
324
364
  function HomeBanner(): ReactElement {
325
- const {title, subtitle, cta} = useContent<HomeBanner>('home-banner', {fallback: fallbackBanner});
365
+ const {title, subtitle, cta} = useContent<HomeBannerContent>('home-banner', {fallback: fallbackBanner});
326
366
 
327
367
  return (
328
368
  <div>
@@ -338,7 +378,7 @@ export default function HomePage(): ReactElement {
338
378
  <Suspense fallback="Personalizing content...">
339
379
  {/* Using the <Slot /> component */}
340
380
  <Slot id="home-banner" fallback={fallbackBanner}>
341
- {({title, subtitle, cta}: HomeBanner) => (
381
+ {({title, subtitle, cta}: HomeBannerContent) => (
342
382
  <div>
343
383
  <strong>{title}</strong>
344
384
  <p>{subtitle}</p>
@@ -354,15 +394,15 @@ export default function HomePage(): ReactElement {
354
394
  }
355
395
  ```
356
396
 
357
- Again, we strongly recommend always providing a value for the `fallback` property. For a full list of the available options,
397
+ Again, we strongly recommend always providing a value for the `fallback` property. For a full list of the available options,
358
398
  please refer to the [API documentation](#component-api-reference).
359
399
 
360
400
  #### 💡 ProTip
361
401
 
362
- In the previous examples, you may have noticed that we specified the content type in the `userFetch` call and in the
363
- `<Slot />` component's render function to have the benefit of strong typing.
402
+ In the previous examples, you may have noticed that we specified the content type in the `userFetch` call and in the
403
+ `<Slot />` component's render function to have the benefit of strong typing.
364
404
 
365
- For an even more robust approach, you can also declare the type of all available slots in a single declaration file
405
+ For an even more robust approach, you can also declare the type of all available slots in a single declaration file
366
406
  using module augmentation as follows:
367
407
 
368
408
  ```ts
@@ -384,16 +424,16 @@ slot IDs and content properties as a bonus:
384
424
 
385
425
  ### Server-side rendering
386
426
 
387
- You can use the same components and hooks on the server-side by simply providing an `initial` state which is used to
388
- pre-render on the server - the personalization happens transparently on the client during the initial render.
427
+ You can use the same components and hooks on the server-side by simply providing an `initial` state which is used to
428
+ pre-render on the server - the personalization happens transparently on the client during the initial render.
389
429
  That means it's SEO friendly and can be cached with no performance overhead.
390
430
 
391
- Notice that the methods exposed by the Plug work only on the client-side. Therefore, if you are using [`useCroct`](#usecroct),
431
+ Notice that the methods exposed by the Plug work only on the client-side. Therefore, if you are using [`useCroct`](#usecroct),
392
432
  the operations have to be executed inside the `useEffect` hook or client-side callbacks, such as `onClick` or `onChange`, for example.
393
433
 
394
434
  ### Accessing the Plug instance
395
435
 
396
- This library is built on top of the PlugJS. You can access the Plug instance through the `useCroct` hook to track events,
436
+ This library is built on top of the PlugJS. You can access the Plug instance through the `useCroct` hook to track events,
397
437
  login and logout users, and more.
398
438
 
399
439
  In the following example we use the `useCroct` to get the Plug instance and set an attribute to the user profile:
@@ -422,7 +462,7 @@ This reference documents all components available in the library.
422
462
  ### &lt;CroctProvider /&gt;
423
463
 
424
464
  The `<CroctProvider />` component leverages [React's Context API](https://reactjs.org/docs/context.html) to
425
- make a configured [Plug instance](https://github.com/croct-tech/plug-js/blob/master/docs/plug.md) available throughout
465
+ make a configured [Plug instance](https://github.com/croct-tech/plug-js/blob/master/docs/plug.md) available throughout
426
466
  a React component tree.
427
467
 
428
468
  #### Properties
@@ -453,7 +493,7 @@ import {CroctProvider} from '@croct/plug-react';
453
493
 
454
494
  function App() {
455
495
  return (
456
- <CroctProvider appId="00000000-0000-0000-0000-000000000000">
496
+ <CroctProvider appId="<APP_ID>">
457
497
  <div>
458
498
  <h1>My first personalized app 🚀</h1>
459
499
  </div>
@@ -462,6 +502,8 @@ function App() {
462
502
  }
463
503
  ```
464
504
 
505
+ > Replace "<APP_ID>" with your public app ID that you can find at Workspaces > Applications > API Keys.
506
+
465
507
  ### &lt;Personalization /&gt;
466
508
 
467
509
  The `<Personalization />` component evaluates and renders a CQL query.
@@ -585,7 +627,7 @@ These are the currently supported options:
585
627
 
586
628
  | Option | Type | Description
587
629
  |--------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
588
- | `fallback` | Result | The value returned when the evaluation fails. If not specified, the hook will throw an exception in case of failures.
630
+ | `fallback` | Result | The value returned when the evaluation fails. If not specified, the hook will throw an exception in case of failures.
589
631
  | `timeout` | number | The maximum evaluation time in milliseconds. Once reached, the evaluation will fail.
590
632
  | `attributes` | JSON | The map of attributes to inject in the evaluation context. For example, passing the attributes `{cities: ['New York', 'San Francisco']}` you can reference them in expressions like `context's cities include location's city`.
591
633
  | `cacheKey` | string | An identifier that allows keeping the cached result separate from other cached items. By default, the cache key is formed from the expression and attributes.
@@ -634,8 +676,17 @@ Here's a simple example showing how to fetch the content for a banner:
634
676
  import {ReactElement} from 'react';
635
677
  import {useContent} from '@croct/plug-react';
636
678
 
637
- function HeroBanner(): ReactElement {
638
- const {title, subtitle} = useContent<HeroBanner>('hero');
679
+ type HomeBannerContent = {
680
+ title: string,
681
+ subtitle: string,
682
+ cta: {
683
+ label: string,
684
+ link: string,
685
+ },
686
+ };
687
+
688
+ export default function HeroBanner(): ReactElement {
689
+ const {title, subtitle} = useContent<HomeBannerContent>('hero');
639
690
 
640
691
  return (
641
692
  <div>
package/index.js CHANGED
@@ -22,9 +22,9 @@ function _objectWithoutPropertiesLoose(source, excluded) {
22
22
  }
23
23
 
24
24
  function isSsr() {
25
- return typeof window === 'undefined';
25
+ return typeof window === 'undefined' || typeof window.document === 'undefined' || typeof window.document.createElement === 'undefined';
26
26
  }
27
- var croct = !isSsr() ? csrPlug__default['default'] : new Proxy(csrPlug__default['default'], {
27
+ var croct = !isSsr() ? csrPlug__default["default"] : new Proxy(csrPlug__default["default"], {
28
28
  get(_, property) {
29
29
  switch (property) {
30
30
  case 'initialized':
@@ -44,13 +44,14 @@ var croct = !isSsr() ? csrPlug__default['default'] : new Proxy(csrPlug__default[
44
44
 
45
45
  });
46
46
 
47
+ var _excluded$4 = ["children"];
47
48
  var CroctContext = /*#__PURE__*/react.createContext(null);
48
49
  CroctContext.displayName = 'CroctContext';
49
50
  var CroctProvider = _ref => {
50
51
  var {
51
52
  children
52
53
  } = _ref,
53
- configuration = _objectWithoutPropertiesLoose(_ref, ["children"]);
54
+ configuration = _objectWithoutPropertiesLoose(_ref, _excluded$4);
54
55
 
55
56
  var parent = react.useContext(CroctContext);
56
57
 
@@ -74,11 +75,10 @@ var CroctProvider = _ref => {
74
75
  croct.unplug();
75
76
  };
76
77
  }, []);
77
- return jsxRuntime.jsx(CroctContext.Provider, Object.assign({
78
- value: context
79
- }, {
78
+ return jsxRuntime.jsx(CroctContext.Provider, {
79
+ value: context,
80
80
  children: children
81
- }), void 0);
81
+ });
82
82
  };
83
83
 
84
84
  class Cache {
@@ -154,6 +154,7 @@ class Cache {
154
154
 
155
155
  }
156
156
 
157
+ var _excluded$3 = ["initial"];
157
158
  var cache = new Cache(60 * 1000);
158
159
  function useLoader(_ref) {
159
160
  var _cache$get;
@@ -161,7 +162,7 @@ function useLoader(_ref) {
161
162
  var {
162
163
  initial
163
164
  } = _ref,
164
- options = _objectWithoutPropertiesLoose(_ref, ["initial"]);
165
+ options = _objectWithoutPropertiesLoose(_ref, _excluded$3);
165
166
 
166
167
  var loadedValue = (_cache$get = cache.get(options.cacheKey)) == null ? void 0 : _cache$get.result;
167
168
  var [value, setValue] = react.useState(loadedValue !== undefined ? loadedValue : initial);
@@ -171,16 +172,17 @@ function useLoader(_ref) {
171
172
  try {
172
173
  setValue(cache.load(options));
173
174
  } catch (result) {
174
- if (typeof (result == null ? void 0 : result.then) !== 'function') {
175
- setValue(undefined);
175
+ if (result instanceof Promise) {
176
+ result.then(resolvedValue => {
177
+ if (!isUnmounted) {
178
+ setValue(resolvedValue);
179
+ }
180
+ });
176
181
  return;
177
182
  }
178
183
 
179
- result.then(resolvedValue => {
180
- if (!isUnmounted) {
181
- setValue(resolvedValue);
182
- }
183
- });
184
+ setValue(undefined);
185
+ return;
184
186
  }
185
187
  }
186
188
 
@@ -206,6 +208,8 @@ function useCroct() {
206
208
  return context.plug;
207
209
  }
208
210
 
211
+ var _excluded$2 = ["cacheKey", "fallback", "initial", "expiration"];
212
+
209
213
  function cleanEvaluationOptions(options) {
210
214
  var result = {};
211
215
 
@@ -231,7 +235,7 @@ function useCsrEvaluation(expression, options) {
231
235
  initial,
232
236
  expiration
233
237
  } = options,
234
- evaluationOptions = _objectWithoutPropertiesLoose(options, ["cacheKey", "fallback", "initial", "expiration"]);
238
+ evaluationOptions = _objectWithoutPropertiesLoose(options, _excluded$2);
235
239
 
236
240
  var croct = useCroct();
237
241
  return useLoader({
@@ -297,30 +301,32 @@ function useSsrContent(_, _temp) {
297
301
 
298
302
  var useContent = isSsr() ? useSsrContent : useCsrContent;
299
303
 
304
+ var _excluded$1 = ["expression", "children"];
300
305
  function Personalization(props) {
301
306
  var {
302
307
  expression,
303
308
  children
304
309
  } = props,
305
- options = _objectWithoutPropertiesLoose(props, ["expression", "children"]);
310
+ options = _objectWithoutPropertiesLoose(props, _excluded$1);
306
311
 
307
312
  var result = useEvaluation(expression, options);
308
313
  return jsxRuntime.jsx(react.Fragment, {
309
314
  children: children(result)
310
- }, void 0);
315
+ });
311
316
  }
312
317
 
318
+ var _excluded = ["id", "children"];
313
319
  var Slot = props => {
314
320
  var {
315
321
  id,
316
322
  children
317
323
  } = props,
318
- options = _objectWithoutPropertiesLoose(props, ["id", "children"]);
324
+ options = _objectWithoutPropertiesLoose(props, _excluded);
319
325
 
320
326
  var data = useContent(id, options);
321
327
  return jsxRuntime.jsx(react.Fragment, {
322
328
  children: children(data)
323
- }, void 0);
329
+ });
324
330
  };
325
331
 
326
332
  exports.CroctContext = CroctContext;
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/ssr-polyfills.ts","../src/CroctProvider.tsx","../src/hooks/Cache.ts","../src/hooks/useLoader.ts","../src/hooks/useCroct.ts","../src/hooks/useEvaluation.ts","../src/hooks/useContent.ts","../src/components/Personalization/index.tsx","../src/components/Slot/index.tsx"],"sourcesContent":["import csrPlug, {Plug} from '@croct/plug';\n\nexport function isSsr(): boolean {\n return typeof window === 'undefined'\n || typeof window.document === 'undefined'\n || typeof window.document.createElement === 'undefined';\n}\n\nexport const croct: Plug = !isSsr() ? csrPlug : new Proxy(csrPlug, {\n get(_, property: keyof Plug) {\n switch (property) {\n case 'initialized':\n return false;\n\n case 'plug':\n return () => {\n // no-op\n };\n\n case 'unplug':\n return () => Promise.resolve();\n\n default:\n throw new Error(\n `Property croct.${String(property)} is not supported on server-side (SSR). Consider refactoring `\n + 'the logic as a side-effect (useEffect) or a client-side callback (onClick, onChange, etc).',\n );\n }\n },\n});\n\n","import {createContext, FunctionComponent, PropsWithChildren, ReactElement, useContext, useEffect, useMemo} from 'react';\nimport {Configuration, Plug} from '@croct/plug';\nimport {croct} from './ssr-polyfills';\n\nexport type CroctProviderProps = PropsWithChildren<Configuration & Required<Pick<Configuration, 'appId'>>>;\n\nexport const CroctContext = createContext<{plug: Plug}|null>(null);\nCroctContext.displayName = 'CroctContext';\n\nexport const CroctProvider: FunctionComponent<CroctProviderProps> = ({children, ...configuration}): ReactElement => {\n const parent = useContext(CroctContext);\n\n if (parent !== null) {\n throw new Error(\n 'You cannot render <CroctProvider> inside another <CroctProvider>. '\n + 'Croct should only be initialized once in the application.',\n );\n }\n\n const context = useMemo(() => ({\n get plug() {\n if (!croct.initialized) {\n croct.plug(configuration);\n }\n\n return croct;\n },\n }), [configuration]);\n\n useEffect(() => {\n croct.plug(configuration);\n\n return () => {\n croct.unplug();\n };\n }, []);\n\n return (\n <CroctContext.Provider value={context}>\n {children}\n </CroctContext.Provider>\n );\n};\n","export type EntryLoader<R> = (...args: any) => Promise<R>;\n\nexport type EntryOptions<R> = {\n cacheKey: string,\n loader: EntryLoader<R>,\n fallback?: R,\n expiration?: number,\n};\n\ntype Entry<R = any> = {\n promise: Promise<any>,\n result?: R,\n dispose: () => void,\n timeout?: number,\n error?: any,\n};\n\nexport class Cache {\n private readonly cache: Record<string, Entry> = {};\n\n private readonly defaultExpiration: number;\n\n public constructor(defaultExpiration: number) {\n this.defaultExpiration = defaultExpiration;\n }\n\n public load<R>(configuration: EntryOptions<R>): R {\n const {cacheKey, loader, fallback, expiration = this.defaultExpiration} = configuration;\n\n const cachedEntry = this.get<R>(cacheKey);\n\n if (cachedEntry !== undefined) {\n if (cachedEntry.error !== undefined) {\n if (fallback !== undefined) {\n return fallback;\n }\n\n throw cachedEntry.error;\n }\n\n if (cachedEntry.result !== undefined) {\n return cachedEntry.result;\n }\n\n throw cachedEntry.promise;\n }\n\n const entry: Entry<R> = {\n dispose: () => {\n if (entry.timeout !== undefined || expiration < 0) {\n return;\n }\n\n entry.timeout = window.setTimeout(\n (): void => {\n delete this.cache[cacheKey];\n },\n expiration,\n );\n },\n promise: loader()\n .then((result): R => {\n entry.result = result;\n\n return result;\n })\n .catch(error => {\n entry.error = error;\n })\n .finally(() => {\n entry.dispose();\n }),\n };\n\n this.cache[cacheKey] = entry;\n\n throw entry.promise;\n }\n\n public get<R>(cacheKey: string): Entry<R>|undefined {\n const entry = this.cache[cacheKey];\n\n if (entry === undefined) {\n return undefined;\n }\n\n if (entry.timeout !== undefined) {\n clearTimeout(entry.timeout);\n\n delete entry.timeout;\n\n entry.dispose();\n }\n\n return entry;\n }\n}\n","import {useEffect, useState} from 'react';\nimport {Cache, EntryOptions} from './Cache';\n\nconst cache = new Cache(60 * 1000);\n\nexport type CacheOptions<R> = EntryOptions<R> & {\n initial?: R,\n};\n\nexport function useLoader<R>({initial, ...options}: CacheOptions<R>): R {\n const loadedValue: R|undefined = cache.get<R>(options.cacheKey)?.result;\n const [value, setValue] = useState(loadedValue !== undefined ? loadedValue : initial);\n const [isUnmounted, setUnmounted] = useState(false);\n\n useEffect(\n () => {\n if (initial !== undefined) {\n try {\n setValue(cache.load(options));\n } catch (result: unknown) {\n if (result instanceof Promise) {\n result.then((resolvedValue: R) => {\n if (!isUnmounted) {\n setValue(resolvedValue);\n }\n });\n\n return;\n }\n\n setValue(undefined);\n\n return;\n }\n }\n\n return () => {\n setUnmounted(true);\n };\n },\n [],\n );\n\n if (value === undefined) {\n return cache.load(options);\n }\n\n return value;\n}\n","import {Plug} from '@croct/plug';\nimport {useContext} from 'react';\nimport {CroctContext} from '../CroctProvider';\n\nexport function useCroct(): Plug {\n const context = useContext(CroctContext);\n\n if (context === null) {\n throw new Error('useCroct() can only be used in the context of a <CroctProvider> component.');\n }\n\n return context.plug;\n}\n","import {JsonValue} from '@croct/plug/sdk/json';\nimport {EvaluationOptions} from '@croct/sdk/facade/evaluatorFacade';\nimport {useLoader} from './useLoader';\nimport {useCroct} from './useCroct';\nimport {isSsr} from '../ssr-polyfills';\n\nfunction cleanEvaluationOptions(options: EvaluationOptions): EvaluationOptions {\n const result: EvaluationOptions = {};\n\n for (const [key, value] of Object.entries(options)) {\n if (value !== undefined) {\n result[key] = value;\n }\n }\n\n return result;\n}\n\nexport type UseEvaluationOptions<I, F> = EvaluationOptions & {\n initial?: I,\n fallback?: F,\n cacheKey?: string,\n expiration?: number,\n};\n\ntype UseEvaluationHook = <T extends JsonValue, I = T, F = T>(\n expression: string,\n options?: UseEvaluationOptions<I, F>,\n) => T | I | F;\n\nfunction useCsrEvaluation<T = JsonValue, I = T, F = T>(\n expression: string,\n options: UseEvaluationOptions<I, F> = {},\n): T | I | F {\n const {cacheKey, fallback, initial, expiration, ...evaluationOptions} = options;\n const croct = useCroct();\n\n return useLoader<T | I | F>({\n cacheKey: `useEvaluation:${cacheKey ?? ''}:${expression}:${JSON.stringify(options.attributes ?? '')}`,\n loader: () => croct.evaluate<T & JsonValue>(expression, cleanEvaluationOptions(evaluationOptions)),\n initial: initial,\n fallback: fallback,\n expiration: expiration,\n });\n}\n\nfunction useSsrEvaluation<T = JsonValue, I = T, F = T>(\n _: string,\n {initial}: UseEvaluationOptions<I, F> = {},\n): T | I | F {\n if (initial === undefined) {\n throw new Error('The initial value is required for server-side rendering (SSR).');\n }\n\n return initial;\n}\n\nexport const useEvaluation: UseEvaluationHook = isSsr() ? useSsrEvaluation : useCsrEvaluation;\n","import {SlotContent, SlotId, SlotMap} from '@croct/plug/fetch';\nimport {NullableJsonObject} from '@croct/plug/sdk/json';\nimport {useLoader} from './useLoader';\nimport {useCroct} from './useCroct';\nimport {isSsr} from '../ssr-polyfills';\n\nexport type UseContentOptions<I, F> = {\n fallback?: F,\n initial?: I,\n cacheKey?: string,\n expiration?: number,\n};\n\nfunction useCsrContent<I, F>(id: SlotId, options: UseContentOptions<I, F> = {}): SlotContent<SlotId> | I | F {\n const {fallback, initial, cacheKey, expiration} = options;\n const croct = useCroct();\n\n return useLoader({\n cacheKey: `useContent:${cacheKey ?? ''}:${id}`,\n loader: () => croct.fetch<SlotContent<SlotId>>(id).then(({payload}) => payload),\n initial: initial,\n fallback: fallback,\n expiration: expiration,\n });\n}\n\nfunction useSsrContent<I, F>(_: SlotId, {initial}: UseContentOptions<I, F> = {}): SlotContent<SlotId> | I | F {\n if (initial === undefined) {\n throw new Error('The initial value is required for server-side rendering (SSR).');\n }\n\n return initial;\n}\n\ntype UseContentHook = {\n <P extends NullableJsonObject, I = P, F = P>(\n id: keyof SlotMap extends never ? string : never,\n options?: UseContentOptions<I, F>\n ): P | I | F,\n\n <S extends keyof SlotMap>(\n id: S,\n options?: UseContentOptions<never, never>\n ): SlotContent<S>,\n\n <I, S extends keyof SlotMap>(\n id: S,\n options?: UseContentOptions<I, never>\n ): SlotContent<S> | I,\n\n <F, S extends keyof SlotMap>(\n id: S,\n options?: UseContentOptions<never, F>\n ): SlotContent<S> | F,\n\n <I, F, S extends keyof SlotMap>(\n id: S,\n options?: UseContentOptions<I, F>\n ): SlotContent<S> | I | F,\n};\n\nexport const useContent: UseContentHook = isSsr() ? useSsrContent : useCsrContent;\n","import {ReactChild, ReactElement, Fragment} from 'react';\nimport {JsonValue} from '@croct/plug/sdk/json';\nimport {UseEvaluationOptions, useEvaluation} from '../../hooks';\n\ntype Renderer<T> = (result: T) => ReactChild;\n\nexport type PersonalizationProps<T extends JsonValue = JsonValue, I = T, F = T> = UseEvaluationOptions<I, F> & {\n expression: string,\n children: Renderer<T | I | F>,\n};\n\nexport function Personalization<T extends JsonValue, I, F>(\n props:\n Extract<T | I | F, JsonValue> extends never\n ? PersonalizationProps\n : PersonalizationProps<T, I, F>,\n): ReactElement;\n\nexport function Personalization<I, F>(props: PersonalizationProps<JsonValue, I, F>): ReactElement {\n const {expression, children, ...options} = props;\n const result = useEvaluation(expression, options);\n\n return (<Fragment>{children(result)}</Fragment>);\n}\n","import {Fragment, ReactChild, ReactElement} from 'react';\nimport {SlotContent, SlotId, SlotMap} from '@croct/plug/fetch';\nimport {NullableJsonObject} from '@croct/plug/sdk/json';\nimport {useContent, UseContentOptions} from '../../hooks';\n\ntype Renderer<P> = (props: P) => ReactChild;\n\nexport type SlotProps<P, I = P, F = P, S extends SlotId = SlotId> = UseContentOptions<I, F> & {\n id: S,\n children: Renderer<P | I | F>,\n};\n\ntype SlotComponent = {\n <P, I, F>(\n props:\n Extract<P | I | F, NullableJsonObject> extends never\n ? SlotProps<NullableJsonObject, never, never, keyof SlotMap extends never ? string : never>\n : SlotProps<P, I, F, keyof SlotMap extends never ? string : never>\n ): ReactElement,\n\n <S extends keyof SlotMap>(props: SlotProps<SlotContent<S>, never, never, S>): ReactElement,\n\n <I, S extends keyof SlotMap>(props: SlotProps<SlotContent<S>, I, never, S>): ReactElement,\n\n <F, S extends keyof SlotMap>(props: SlotProps<SlotContent<S>, never, F, S>): ReactElement,\n\n <I, F, S extends keyof SlotMap>(props: SlotProps<SlotContent<S>, I, F, S>): ReactElement,\n\n (props: SlotProps<void, void, void>): ReactElement,\n};\n\nexport const Slot: SlotComponent = <I, F>(props: SlotProps<NullableJsonObject, I, F>): ReactElement => {\n const {id, children, ...options} = props;\n const data: SlotContent<SlotId> | I | F = useContent(id, options);\n\n return <Fragment>{children(data)}</Fragment>;\n};\n"],"names":["isSsr","window","document","createElement","croct","csrPlug","Proxy","get","_","property","Promise","resolve","Error","String","CroctContext","createContext","displayName","CroctProvider","children","configuration","_excluded","parent","useContext","context","useMemo","plug","initialized","useEffect","unplug","_jsx","Provider","value","Cache","constructor","defaultExpiration","cache","load","cacheKey","loader","fallback","expiration","cachedEntry","undefined","error","result","promise","entry","dispose","timeout","setTimeout","then","catch","finally","clearTimeout","useLoader","initial","options","loadedValue","setValue","useState","isUnmounted","setUnmounted","resolvedValue","useCroct","cleanEvaluationOptions","key","Object","entries","useCsrEvaluation","expression","evaluationOptions","JSON","stringify","attributes","evaluate","useSsrEvaluation","useEvaluation","useCsrContent","id","fetch","payload","useSsrContent","useContent","Personalization","props","Fragment","Slot","data"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;SAEgBA,QAAK;AACjB,EAAA,OAAO,OAAOC,MAAP,KAAkB,WAAlB,IACA,OAAOA,MAAM,CAACC,QAAd,KAA2B,WAD3B,IAEA,OAAOD,MAAM,CAACC,QAAP,CAAgBC,aAAvB,KAAyC,WAFhD,CAAA;AAGH,CAAA;AAEM,IAAMC,KAAK,GAAS,CAACJ,KAAK,EAAN,GAAWK,2BAAX,GAAqB,IAAIC,KAAJ,CAAUD,2BAAV,EAAmB;AAC/DE,EAAAA,GAAG,CAACC,CAAD,EAAIC,QAAJ,EAAwB;AACvB,IAAA,QAAQA,QAAR;AACI,MAAA,KAAK,aAAL;AACI,QAAA,OAAO,KAAP,CAAA;;AAEJ,MAAA,KAAK,MAAL;AACI,QAAA,OAAO,MAAK;SAAZ,CAAA;;AAIJ,MAAA,KAAK,QAAL;AACI,QAAA,OAAO,MAAMC,OAAO,CAACC,OAAR,EAAb,CAAA;;AAEJ,MAAA;QACI,MAAM,IAAIC,KAAJ,CACF,iBAAkBC,GAAAA,MAAM,CAACJ,QAAD,CAAxB,GACE,+DAAA,GAAA,4FAFA,CAAN,CAAA;AAbR,KAAA;AAkBH,GAAA;;AApB8D,CAAnB,CAAzC;;;ICFMK,YAAY,gBAAGC,mBAAa,CAAoB,IAApB,EAAlC;AACPD,YAAY,CAACE,WAAb,GAA2B,cAA3B,CAAA;AAEO,IAAMC,aAAa,GAA0C,IAA+C,IAAA;EAAA,IAA9C;AAACC,IAAAA,QAAAA;GAA6C,GAAA,IAAA;AAAA,MAAhCC,aAAgC,GAAA,6BAAA,CAAA,IAAA,EAAAC,WAAA,CAAA,CAAA;;AAC/G,EAAA,IAAMC,MAAM,GAAGC,gBAAU,CAACR,YAAD,CAAzB,CAAA;;EAEA,IAAIO,MAAM,KAAK,IAAf,EAAqB;AACjB,IAAA,MAAM,IAAIT,KAAJ,CACF,oEAAA,GACE,2DAFA,CAAN,CAAA;AAIH,GAAA;;AAED,EAAA,IAAMW,OAAO,GAAGC,aAAO,CAAC,OAAO;AAC3B,IAAA,IAAIC,IAAJ,GAAQ;AACJ,MAAA,IAAI,CAACrB,KAAK,CAACsB,WAAX,EAAwB;QACpBtB,KAAK,CAACqB,IAAN,CAAWN,aAAX,CAAA,CAAA;AACH,OAAA;;AAED,MAAA,OAAOf,KAAP,CAAA;AACH,KAAA;;AAP0B,GAAP,CAAD,EAQnB,CAACe,aAAD,CARmB,CAAvB,CAAA;AAUAQ,EAAAA,eAAS,CAAC,MAAK;IACXvB,KAAK,CAACqB,IAAN,CAAWN,aAAX,CAAA,CAAA;AAEA,IAAA,OAAO,MAAK;AACRf,MAAAA,KAAK,CAACwB,MAAN,EAAA,CAAA;KADJ,CAAA;GAHK,EAMN,EANM,CAAT,CAAA;AAQA,EAAA,OACIC,cAAA,CAACf,YAAY,CAACgB,QAAd,EAAsB;AAACC,IAAAA,KAAK,EAAER,OAAR;AAAeL,IAAAA,QAAA,EAChCA,QAAAA;AADiB,GAAtB,CADJ,CAAA;AAKH;;MCzBYc,MAAK;EAKdC,WAAA,CAAmBC,iBAAnB,EAA4C;IAAA,IAJ3BC,CAAAA,KAI2B,GAJI,EAIJ,CAAA;AAAA,IAAA,IAAA,CAF3BD,iBAE2B,GAAA,KAAA,CAAA,CAAA;IACxC,IAAKA,CAAAA,iBAAL,GAAyBA,iBAAzB,CAAA;AACH,GAAA;;EAEME,IAAI,CAAIjB,aAAJ,EAAkC;IACzC,IAAM;MAACkB,QAAD;MAAWC,MAAX;MAAmBC,QAAnB;AAA6BC,MAAAA,UAAU,GAAG,IAAKN,CAAAA,iBAAAA;AAA/C,KAAA,GAAoEf,aAA1E,CAAA;AAEA,IAAA,IAAMsB,WAAW,GAAG,IAAA,CAAKlC,GAAL,CAAY8B,QAAZ,CAApB,CAAA;;IAEA,IAAII,WAAW,KAAKC,SAApB,EAA+B;AAC3B,MAAA,IAAID,WAAW,CAACE,KAAZ,KAAsBD,SAA1B,EAAqC;QACjC,IAAIH,QAAQ,KAAKG,SAAjB,EAA4B;AACxB,UAAA,OAAOH,QAAP,CAAA;AACH,SAAA;;QAED,MAAME,WAAW,CAACE,KAAlB,CAAA;AACH,OAAA;;AAED,MAAA,IAAIF,WAAW,CAACG,MAAZ,KAAuBF,SAA3B,EAAsC;QAClC,OAAOD,WAAW,CAACG,MAAnB,CAAA;AACH,OAAA;;MAED,MAAMH,WAAW,CAACI,OAAlB,CAAA;AACH,KAAA;;AAED,IAAA,IAAMC,KAAK,GAAa;AACpBC,MAAAA,OAAO,EAAE,MAAK;QACV,IAAID,KAAK,CAACE,OAAN,KAAkBN,SAAlB,IAA+BF,UAAU,GAAG,CAAhD,EAAmD;AAC/C,UAAA,OAAA;AACH,SAAA;;AAEDM,QAAAA,KAAK,CAACE,OAAN,GAAgB/C,MAAM,CAACgD,UAAP,CACZ,MAAW;AACP,UAAA,OAAO,IAAKd,CAAAA,KAAL,CAAWE,QAAX,CAAP,CAAA;SAFQ,EAIZG,UAJY,CAAhB,CAAA;OANgB;AAapBK,MAAAA,OAAO,EAAEP,MAAM,EAAA,CACVY,IADI,CACEN,MAAD,IAAc;QAChBE,KAAK,CAACF,MAAN,GAAeA,MAAf,CAAA;AAEA,QAAA,OAAOA,MAAP,CAAA;AACH,OALI,CAMJO,CAAAA,KANI,CAMER,KAAK,IAAG;QACXG,KAAK,CAACH,KAAN,GAAcA,KAAd,CAAA;OAPC,CAAA,CASJS,OATI,CASI,MAAK;AACVN,QAAAA,KAAK,CAACC,OAAN,EAAA,CAAA;OAVC,CAAA;KAbb,CAAA;AA2BA,IAAA,IAAA,CAAKZ,KAAL,CAAWE,QAAX,CAAA,GAAuBS,KAAvB,CAAA;IAEA,MAAMA,KAAK,CAACD,OAAZ,CAAA;AACH,GAAA;;EAEMtC,GAAG,CAAI8B,QAAJ,EAAoB;AAC1B,IAAA,IAAMS,KAAK,GAAG,IAAA,CAAKX,KAAL,CAAWE,QAAX,CAAd,CAAA;;IAEA,IAAIS,KAAK,KAAKJ,SAAd,EAAyB;AACrB,MAAA,OAAOA,SAAP,CAAA;AACH,KAAA;;AAED,IAAA,IAAII,KAAK,CAACE,OAAN,KAAkBN,SAAtB,EAAiC;AAC7BW,MAAAA,YAAY,CAACP,KAAK,CAACE,OAAP,CAAZ,CAAA;MAEA,OAAOF,KAAK,CAACE,OAAb,CAAA;AAEAF,MAAAA,KAAK,CAACC,OAAN,EAAA,CAAA;AACH,KAAA;;AAED,IAAA,OAAOD,KAAP,CAAA;AACH,GAAA;;AA9Ea;;;ACdlB,IAAMX,KAAK,GAAG,IAAIH,KAAJ,CAAU,EAAA,GAAK,IAAf,CAAd,CAAA;AAMM,SAAUsB,SAAV,CAA6D,IAAA,EAAA;AAAA,EAAA,IAAA,UAAA,CAAA;;EAAA,IAAtC;AAACC,IAAAA,OAAAA;GAAqC,GAAA,IAAA;AAAA,MAAzBC,OAAyB,GAAA,6BAAA,CAAA,IAAA,EAAApC,WAAA,CAAA,CAAA;;AAC/D,EAAA,IAAMqC,WAAW,GAAA,CAAA,UAAA,GAAgBtB,KAAK,CAAC5B,GAAN,CAAaiD,OAAO,CAACnB,QAArB,CAAhB,KAAgB,IAAA,GAAA,KAAA,CAAA,GAAA,UAAA,CAAgCO,MAAjE,CAAA;AACA,EAAA,IAAM,CAACb,KAAD,EAAQ2B,QAAR,IAAoBC,cAAQ,CAACF,WAAW,KAAKf,SAAhB,GAA4Be,WAA5B,GAA0CF,OAA3C,CAAlC,CAAA;EACA,IAAM,CAACK,WAAD,EAAcC,YAAd,IAA8BF,cAAQ,CAAC,KAAD,CAA5C,CAAA;AAEAhC,EAAAA,eAAS,CACL,MAAK;IACD,IAAI4B,OAAO,KAAKb,SAAhB,EAA2B;MACvB,IAAI;AACAgB,QAAAA,QAAQ,CAACvB,KAAK,CAACC,IAAN,CAAWoB,OAAX,CAAD,CAAR,CAAA;OADJ,CAEE,OAAOZ,MAAP,EAAwB;QACtB,IAAIA,MAAM,YAAYlC,OAAtB,EAA+B;AAC3BkC,UAAAA,MAAM,CAACM,IAAP,CAAaY,aAAD,IAAqB;YAC7B,IAAI,CAACF,WAAL,EAAkB;cACdF,QAAQ,CAACI,aAAD,CAAR,CAAA;AACH,aAAA;WAHL,CAAA,CAAA;AAMA,UAAA,OAAA;AACH,SAAA;;QAEDJ,QAAQ,CAAChB,SAAD,CAAR,CAAA;AAEA,QAAA,OAAA;AACH,OAAA;AACJ,KAAA;;AAED,IAAA,OAAO,MAAK;MACRmB,YAAY,CAAC,IAAD,CAAZ,CAAA;KADJ,CAAA;GAtBC,EA0BL,EA1BK,CAAT,CAAA;;EA6BA,IAAI9B,KAAK,KAAKW,SAAd,EAAyB;AACrB,IAAA,OAAOP,KAAK,CAACC,IAAN,CAAWoB,OAAX,CAAP,CAAA;AACH,GAAA;;AAED,EAAA,OAAOzB,KAAP,CAAA;AACH;;SC5CegC,WAAQ;AACpB,EAAA,IAAMxC,OAAO,GAAGD,gBAAU,CAACR,YAAD,CAA1B,CAAA;;EAEA,IAAIS,OAAO,KAAK,IAAhB,EAAsB;AAClB,IAAA,MAAM,IAAIX,KAAJ,CAAU,4EAAV,CAAN,CAAA;AACH,GAAA;;EAED,OAAOW,OAAO,CAACE,IAAf,CAAA;AACH;;;;ACND,SAASuC,sBAAT,CAAgCR,OAAhC,EAA0D;EACtD,IAAMZ,MAAM,GAAsB,EAAlC,CAAA;;AAEA,EAAA,KAAK,IAAM,CAACqB,GAAD,EAAMlC,KAAN,CAAX,IAA2BmC,MAAM,CAACC,OAAP,CAAeX,OAAf,CAA3B,EAAoD;IAChD,IAAIzB,KAAK,KAAKW,SAAd,EAAyB;AACrBE,MAAAA,MAAM,CAACqB,GAAD,CAAN,GAAclC,KAAd,CAAA;AACH,KAAA;AACJ,GAAA;;AAED,EAAA,OAAOa,MAAP,CAAA;AACH,CAAA;;AAcD,SAASwB,gBAAT,CACIC,UADJ,EAEIb,OAFJ,EAE4C;AAAA,EAAA,IAAA,mBAAA,CAAA;;AAAA,EAAA,IAAxCA,OAAwC,KAAA,KAAA,CAAA,EAAA;AAAxCA,IAAAA,OAAwC,GAAF,EAAE,CAAA;AAAA,GAAA;;EAExC,IAAM;IAACnB,QAAD;IAAWE,QAAX;IAAqBgB,OAArB;AAA8Bf,IAAAA,UAAAA;AAA9B,GAAA,GAAkEgB,OAAxE;MAAmDc,iBAAnD,iCAAwEd,OAAxE,EAAApC,WAAA,CAAA,CAAA;;EACA,IAAMhB,KAAK,GAAG2D,QAAQ,EAAtB,CAAA;AAEA,EAAA,OAAOT,SAAS,CAAY;AACxBjB,IAAAA,QAAQ,sBAAmBA,QAAnB,IAAA,IAAA,GAAmBA,QAAnB,GAA+B,EAA/B,UAAqCgC,UAArC,GAAA,GAAA,GAAmDE,IAAI,CAACC,SAAL,CAAehB,CAAAA,mBAAAA,GAAAA,OAAO,CAACiB,UAAvB,KAAA,IAAA,GAAA,mBAAA,GAAqC,EAArC,CADnC;AAExBnC,IAAAA,MAAM,EAAE,MAAMlC,KAAK,CAACsE,QAAN,CAA8BL,UAA9B,EAA0CL,sBAAsB,CAACM,iBAAD,CAAhE,CAFU;AAGxBf,IAAAA,OAAO,EAAEA,OAHe;AAIxBhB,IAAAA,QAAQ,EAAEA,QAJc;AAKxBC,IAAAA,UAAU,EAAEA,UAAAA;AALY,GAAZ,CAAhB,CAAA;AAOH,CAAA;;AAED,SAASmC,gBAAT,CACInE,CADJ,EAE8C,KAAA,EAAA;EAAA,IAA1C;AAAC+C,IAAAA,OAAAA;AAAD,GAA0C,sBAAF,EAAE,GAAA,KAAA,CAAA;;EAE1C,IAAIA,OAAO,KAAKb,SAAhB,EAA2B;AACvB,IAAA,MAAM,IAAI9B,KAAJ,CAAU,gEAAV,CAAN,CAAA;AACH,GAAA;;AAED,EAAA,OAAO2C,OAAP,CAAA;AACH,CAAA;;IAEYqB,aAAa,GAAsB5E,KAAK,EAAK2E,GAAAA,gBAAL,GAAwBP;;AC5C7E,SAASS,aAAT,CAA6BC,EAA7B,EAAyCtB,OAAzC,EAA8E;AAAA,EAAA,IAArCA,OAAqC,KAAA,KAAA,CAAA,EAAA;AAArCA,IAAAA,OAAqC,GAAF,EAAE,CAAA;AAAA,GAAA;;EAC1E,IAAM;IAACjB,QAAD;IAAWgB,OAAX;IAAoBlB,QAApB;AAA8BG,IAAAA,UAAAA;AAA9B,GAAA,GAA4CgB,OAAlD,CAAA;EACA,IAAMpD,KAAK,GAAG2D,QAAQ,EAAtB,CAAA;AAEA,EAAA,OAAOT,SAAS,CAAC;AACbjB,IAAAA,QAAQ,mBAAgBA,QAAhB,IAAA,IAAA,GAAgBA,QAAhB,GAA4B,EAA5B,UAAkCyC,EAD7B;IAEbxC,MAAM,EAAE,MAAMlC,KAAK,CAAC2E,KAAN,CAAiCD,EAAjC,CAAqC5B,CAAAA,IAArC,CAA0C,IAAA,IAAA;MAAA,IAAC;AAAC8B,QAAAA,OAAAA;OAAF,GAAA,IAAA,CAAA;AAAA,MAAA,OAAeA,OAAf,CAAA;AAAA,KAA1C,CAFD;AAGbzB,IAAAA,OAAO,EAAEA,OAHI;AAIbhB,IAAAA,QAAQ,EAAEA,QAJG;AAKbC,IAAAA,UAAU,EAAEA,UAAAA;AALC,GAAD,CAAhB,CAAA;AAOH,CAAA;;AAED,SAASyC,aAAT,CAA6BzE,CAA7B,EAA+E,KAAA,EAAA;EAAA,IAAvC;AAAC+C,IAAAA,OAAAA;AAAD,GAAuC,sBAAF,EAAE,GAAA,KAAA,CAAA;;EAC3E,IAAIA,OAAO,KAAKb,SAAhB,EAA2B;AACvB,IAAA,MAAM,IAAI9B,KAAJ,CAAU,gEAAV,CAAN,CAAA;AACH,GAAA;;AAED,EAAA,OAAO2C,OAAP,CAAA;AACH,CAAA;;IA6BY2B,UAAU,GAAmBlF,KAAK,EAAKiF,GAAAA,aAAL,GAAqBJ;;;AC3C9D,SAAUM,eAAV,CAAgCC,KAAhC,EAA4E;EAC9E,IAAM;IAACf,UAAD;AAAanD,IAAAA,QAAAA;AAAb,GAAA,GAAqCkE,KAA3C;MAAgC5B,OAAhC,iCAA2C4B,KAA3C,EAAAhE,WAAA,CAAA,CAAA;;AACA,EAAA,IAAMwB,MAAM,GAAGgC,aAAa,CAACP,UAAD,EAAab,OAAb,CAA5B,CAAA;EAEA,OAAQ3B,cAAC,CAAAwD,cAAA,EAAU;IAAAnE,QAAA,EAAAA,QAAQ,CAAC0B,MAAD,CAAA;AAAR,GAAV,CAAT,CAAA;AACH;;;ACQY0C,IAAAA,IAAI,GAAyBF,KAAP,IAAmE;EAClG,IAAM;IAACN,EAAD;AAAK5D,IAAAA,QAAAA;AAAL,GAAA,GAA6BkE,KAAnC;MAAwB5B,OAAxB,iCAAmC4B,KAAnC,EAAA,SAAA,CAAA,CAAA;;AACA,EAAA,IAAMG,IAAI,GAAgCL,UAAU,CAACJ,EAAD,EAAKtB,OAAL,CAApD,CAAA;EAEA,OAAO3B,cAAA,CAACwD,cAAD,EAAW;IAAAnE,QAAA,EAAAA,QAAQ,CAACqE,IAAD,CAAA;AAAR,GAAX,CAAP,CAAA;AACH;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@croct/plug-react",
3
- "version": "0.3.0",
3
+ "version": "0.4.2",
4
4
  "description": "React components and hooks to plug your React applications into Croct.",
5
5
  "author": {
6
6
  "name": "Croct",
@@ -38,50 +38,55 @@
38
38
  "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@croct/plug": "^0.9.0"
41
+ "@croct/plug": "^0.10.1"
42
42
  },
43
43
  "devDependencies": {
44
- "@babel/core": "^7.13.13",
45
- "@babel/preset-env": "^7.13.12",
46
- "@babel/preset-react": "^7.13.13",
47
- "@babel/preset-typescript": "^7.13.0",
48
- "@storybook/addon-actions": "^6.2.3",
49
- "@storybook/addon-essentials": "^6.4.13",
50
- "@storybook/addon-links": "^6.2.3",
51
- "@storybook/react": "^6.4.13",
52
- "@testing-library/jest-dom": "^5.12.0",
53
- "@testing-library/react": "^11.2.7",
54
- "@testing-library/react-hooks": "^7.0.0",
55
- "@types/jest": "^26.0.22",
56
- "@types/node": "^14.14.37",
57
- "@types/react": "^16.0.0",
58
- "@types/react-dom": "^16.0.0",
59
- "@typescript-eslint/eslint-plugin": "^4.25.0",
60
- "@typescript-eslint/parser": "^4.25.0",
44
+ "@babel/core": "^7.18.5",
45
+ "@babel/preset-env": "^7.18.2",
46
+ "@babel/preset-react": "^7.17.12",
47
+ "@babel/preset-typescript": "^7.17.12",
48
+ "@storybook/addon-actions": "^6.5.9",
49
+ "@storybook/addon-essentials": "^6.5.9",
50
+ "@storybook/addon-links": "^6.5.9",
51
+ "@storybook/builder-webpack5": "^6.5.9",
52
+ "@storybook/manager-webpack5": "^6.5.9",
53
+ "@storybook/react": "^6.5.9",
54
+ "@testing-library/jest-dom": "^5.16.4",
55
+ "@testing-library/react": "^13.3.0",
56
+ "@testing-library/react-hooks": "^8.0.0",
57
+ "@types/jest": "^28.1.2",
58
+ "@types/node": "^16.11.41",
59
+ "@types/react": "^18.0.14",
60
+ "@types/react-dom": "^18.0.5",
61
+ "@typescript-eslint/eslint-plugin": "^5.28.0",
62
+ "@typescript-eslint/parser": "^5.28.0",
61
63
  "babel-eslint": "^10.1.0",
62
- "babel-jest": "^26.6.3",
63
- "babel-loader": "8.1.0",
64
- "eslint": "^7.27.0",
65
- "eslint-config-airbnb-base": "^14.2.1",
66
- "eslint-config-standard": "^16.0.2",
64
+ "babel-jest": "^28.1.1",
65
+ "babel-loader": "^8.2.5",
66
+ "eslint": "^8.17.0",
67
+ "eslint-config-airbnb-base": "^15.0.0",
68
+ "eslint-config-standard": "^17.0.0",
67
69
  "eslint-config-standard-react": "^11.0.1",
68
- "eslint-plugin-import": "^2.22.1",
69
- "eslint-plugin-jest": "^24.3.2",
70
+ "eslint-plugin-import": "^2.26.0",
71
+ "eslint-plugin-jest": "^26.5.3",
72
+ "eslint-plugin-n": "^15.2.3",
70
73
  "eslint-plugin-node": "^11.1.0",
71
- "eslint-plugin-promise": "^4.3.1",
72
- "eslint-plugin-react": "^7.23.2",
74
+ "eslint-plugin-promise": "^6.0.0",
75
+ "eslint-plugin-react": "^7.30.0",
73
76
  "eslint-plugin-standard": "^5.0.0",
74
- "jest": "26.6.0",
75
- "jest-environment-node": "^26.6.2",
76
- "microbundle": "^0.13.3",
77
- "react": "^16.0.0",
78
- "react-dom": "^16.0.0",
79
- "ts-node": "^10.0.0",
80
- "typescript": "^4.1.5",
81
- "webpack": "^5.39.1"
77
+ "jest": "^28.1.1",
78
+ "jest-environment-jsdom": "^28.1.1",
79
+ "jest-environment-node": "^28.1.1",
80
+ "microbundle": "^0.15.0",
81
+ "react": "^18.2.0",
82
+ "react-dom": "^18.2.0",
83
+ "ts-node": "^10.8.1",
84
+ "typescript": "^4.7.3",
85
+ "webpack": "^5.73.0"
82
86
  },
83
87
  "files": [
84
88
  "**/*.js",
89
+ "**/*.js.map",
85
90
  "**/*.ts"
86
91
  ]
87
92
  }
package/index.modern.js DELETED
@@ -1,308 +0,0 @@
1
- import { jsx } from 'react/jsx-runtime';
2
- import { createContext, useContext, useMemo, useEffect, useState, Fragment } from 'react';
3
- import csrPlug from '@croct/plug';
4
-
5
- function _objectWithoutPropertiesLoose(source, excluded) {
6
- if (source == null) return {};
7
- var target = {};
8
- var sourceKeys = Object.keys(source);
9
- var key, i;
10
-
11
- for (i = 0; i < sourceKeys.length; i++) {
12
- key = sourceKeys[i];
13
- if (excluded.indexOf(key) >= 0) continue;
14
- target[key] = source[key];
15
- }
16
-
17
- return target;
18
- }
19
-
20
- function isSsr() {
21
- return typeof window === 'undefined';
22
- }
23
- const croct = !isSsr() ? csrPlug : new Proxy(csrPlug, {
24
- get(_, property) {
25
- switch (property) {
26
- case 'initialized':
27
- return false;
28
-
29
- case 'plug':
30
- return () => {// no-op
31
- };
32
-
33
- case 'unplug':
34
- return () => Promise.resolve();
35
-
36
- default:
37
- throw new Error(`Property croct.${String(property)} is not supported on server-side (SSR). Consider refactoring ` + 'the logic as a side-effect (useEffect) or a client-side callback (onClick, onChange, etc).');
38
- }
39
- }
40
-
41
- });
42
-
43
- const CroctContext = /*#__PURE__*/createContext(null);
44
- CroctContext.displayName = 'CroctContext';
45
- const CroctProvider = _ref => {
46
- let {
47
- children
48
- } = _ref,
49
- configuration = _objectWithoutPropertiesLoose(_ref, ["children"]);
50
-
51
- const parent = useContext(CroctContext);
52
-
53
- if (parent !== null) {
54
- throw new Error('You cannot render <CroctProvider> inside another <CroctProvider>. ' + 'Croct should only be initialized once in the application.');
55
- }
56
-
57
- const context = useMemo(() => ({
58
- get plug() {
59
- if (!croct.initialized) {
60
- croct.plug(configuration);
61
- }
62
-
63
- return croct;
64
- }
65
-
66
- }), [configuration]);
67
- useEffect(() => {
68
- croct.plug(configuration);
69
- return () => {
70
- croct.unplug();
71
- };
72
- }, []);
73
- return jsx(CroctContext.Provider, Object.assign({
74
- value: context
75
- }, {
76
- children: children
77
- }), void 0);
78
- };
79
-
80
- class Cache {
81
- constructor(defaultExpiration) {
82
- this.cache = {};
83
- this.defaultExpiration = void 0;
84
- this.defaultExpiration = defaultExpiration;
85
- }
86
-
87
- load(configuration) {
88
- const {
89
- cacheKey,
90
- loader,
91
- fallback,
92
- expiration = this.defaultExpiration
93
- } = configuration;
94
- const cachedEntry = this.get(cacheKey);
95
-
96
- if (cachedEntry !== undefined) {
97
- if (cachedEntry.error !== undefined) {
98
- if (fallback !== undefined) {
99
- return fallback;
100
- }
101
-
102
- throw cachedEntry.error;
103
- }
104
-
105
- if (cachedEntry.result !== undefined) {
106
- return cachedEntry.result;
107
- }
108
-
109
- throw cachedEntry.promise;
110
- }
111
-
112
- const entry = {
113
- dispose: () => {
114
- if (entry.timeout !== undefined || expiration < 0) {
115
- return;
116
- }
117
-
118
- entry.timeout = window.setTimeout(() => {
119
- delete this.cache[cacheKey];
120
- }, expiration);
121
- },
122
- promise: loader().then(result => {
123
- entry.result = result;
124
- return result;
125
- }).catch(error => {
126
- entry.error = error;
127
- }).finally(() => {
128
- entry.dispose();
129
- })
130
- };
131
- this.cache[cacheKey] = entry;
132
- throw entry.promise;
133
- }
134
-
135
- get(cacheKey) {
136
- const entry = this.cache[cacheKey];
137
-
138
- if (entry === undefined) {
139
- return undefined;
140
- }
141
-
142
- if (entry.timeout !== undefined) {
143
- clearTimeout(entry.timeout);
144
- delete entry.timeout;
145
- entry.dispose();
146
- }
147
-
148
- return entry;
149
- }
150
-
151
- }
152
-
153
- const cache = new Cache(60 * 1000);
154
- function useLoader(_ref) {
155
- var _cache$get;
156
-
157
- let {
158
- initial
159
- } = _ref,
160
- options = _objectWithoutPropertiesLoose(_ref, ["initial"]);
161
-
162
- const loadedValue = (_cache$get = cache.get(options.cacheKey)) == null ? void 0 : _cache$get.result;
163
- const [value, setValue] = useState(loadedValue !== undefined ? loadedValue : initial);
164
- const [isUnmounted, setUnmounted] = useState(false);
165
- useEffect(() => {
166
- if (initial !== undefined) {
167
- try {
168
- setValue(cache.load(options));
169
- } catch (result) {
170
- if (typeof (result == null ? void 0 : result.then) !== 'function') {
171
- setValue(undefined);
172
- return;
173
- }
174
-
175
- result.then(resolvedValue => {
176
- if (!isUnmounted) {
177
- setValue(resolvedValue);
178
- }
179
- });
180
- }
181
- }
182
-
183
- return () => {
184
- setUnmounted(true);
185
- };
186
- }, []);
187
-
188
- if (value === undefined) {
189
- return cache.load(options);
190
- }
191
-
192
- return value;
193
- }
194
-
195
- function useCroct() {
196
- const context = useContext(CroctContext);
197
-
198
- if (context === null) {
199
- throw new Error('useCroct() can only be used in the context of a <CroctProvider> component.');
200
- }
201
-
202
- return context.plug;
203
- }
204
-
205
- function cleanEvaluationOptions(options) {
206
- const result = {};
207
-
208
- for (const [key, value] of Object.entries(options)) {
209
- if (value !== undefined) {
210
- result[key] = value;
211
- }
212
- }
213
-
214
- return result;
215
- }
216
-
217
- function useCsrEvaluation(expression, options = {}) {
218
- var _options$attributes;
219
-
220
- const {
221
- cacheKey,
222
- fallback,
223
- initial,
224
- expiration
225
- } = options,
226
- evaluationOptions = _objectWithoutPropertiesLoose(options, ["cacheKey", "fallback", "initial", "expiration"]);
227
-
228
- const croct = useCroct();
229
- return useLoader({
230
- cacheKey: `useEvaluation:${cacheKey != null ? cacheKey : ''}:${expression}:${JSON.stringify((_options$attributes = options.attributes) != null ? _options$attributes : '')}`,
231
- loader: () => croct.evaluate(expression, cleanEvaluationOptions(evaluationOptions)),
232
- initial: initial,
233
- fallback: fallback,
234
- expiration: expiration
235
- });
236
- }
237
-
238
- function useSsrEvaluation(_, {
239
- initial
240
- } = {}) {
241
- if (initial === undefined) {
242
- throw new Error('The initial value is required for server-side rendering (SSR).');
243
- }
244
-
245
- return initial;
246
- }
247
-
248
- const useEvaluation = isSsr() ? useSsrEvaluation : useCsrEvaluation;
249
-
250
- function useCsrContent(id, options = {}) {
251
- const {
252
- fallback,
253
- initial,
254
- cacheKey,
255
- expiration
256
- } = options;
257
- const croct = useCroct();
258
- return useLoader({
259
- cacheKey: `useContent:${cacheKey != null ? cacheKey : ''}:${id}`,
260
- loader: () => croct.fetch(id).then(({
261
- payload
262
- }) => payload),
263
- initial: initial,
264
- fallback: fallback,
265
- expiration: expiration
266
- });
267
- }
268
-
269
- function useSsrContent(_, {
270
- initial
271
- } = {}) {
272
- if (initial === undefined) {
273
- throw new Error('The initial value is required for server-side rendering (SSR).');
274
- }
275
-
276
- return initial;
277
- }
278
-
279
- const useContent = isSsr() ? useSsrContent : useCsrContent;
280
-
281
- function Personalization(props) {
282
- const {
283
- expression,
284
- children
285
- } = props,
286
- options = _objectWithoutPropertiesLoose(props, ["expression", "children"]);
287
-
288
- const result = useEvaluation(expression, options);
289
- return jsx(Fragment, {
290
- children: children(result)
291
- }, void 0);
292
- }
293
-
294
- const Slot = props => {
295
- const {
296
- id,
297
- children
298
- } = props,
299
- options = _objectWithoutPropertiesLoose(props, ["id", "children"]);
300
-
301
- const data = useContent(id, options);
302
- return jsx(Fragment, {
303
- children: children(data)
304
- }, void 0);
305
- };
306
-
307
- export { CroctContext, CroctProvider, Personalization, Slot, useContent, useCroct, useEvaluation };
308
- //# sourceMappingURL=index.modern.js.map