fontdue-js 2.22.3 → 2.23.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 (73) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/__generated__/CartItemAdditionalLicenses_orderItem.graphql.d.ts +4 -1
  3. package/dist/__generated__/CartItemAdditionalLicenses_orderItem.graphql.js +34 -22
  4. package/dist/__generated__/CartOrderCompleteOrderMutation.graphql.d.ts +1 -1
  5. package/dist/__generated__/CartOrderCompleteOrderMutation.graphql.js +13 -4
  6. package/dist/__generated__/CartOrderRemoveDiscountMutation.graphql.d.ts +1 -1
  7. package/dist/__generated__/CartOrderRemoveDiscountMutation.graphql.js +13 -4
  8. package/dist/__generated__/CartOrderUpdateMutation.graphql.d.ts +1 -1
  9. package/dist/__generated__/CartOrderUpdateMutation.graphql.js +13 -4
  10. package/dist/__generated__/CartQuery.graphql.d.ts +1 -1
  11. package/dist/__generated__/CartQuery.graphql.js +13 -4
  12. package/dist/__generated__/CartStateUpdateMutation.graphql.d.ts +1 -1
  13. package/dist/__generated__/CartStateUpdateMutation.graphql.js +13 -4
  14. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.d.ts +1 -1
  15. package/dist/__generated__/CheckoutUpdateCustomerMutation.graphql.js +13 -4
  16. package/dist/__generated__/CheckoutUpdateOrderMutation.graphql.d.ts +1 -1
  17. package/dist/__generated__/CheckoutUpdateOrderMutation.graphql.js +13 -4
  18. package/dist/__generated__/FontdueProviderQuery.graphql.d.ts +19 -0
  19. package/dist/__generated__/FontdueProviderQuery.graphql.js +140 -0
  20. package/dist/__generated__/PrecartAddToCartMutation.graphql.d.ts +1 -1
  21. package/dist/__generated__/PrecartAddToCartMutation.graphql.js +34 -25
  22. package/dist/__generated__/PrecartQuery.graphql.d.ts +1 -1
  23. package/dist/__generated__/PrecartQuery.graphql.js +17 -7
  24. package/dist/__generated__/Precart_collection.graphql.d.ts +4 -1
  25. package/dist/__generated__/Precart_collection.graphql.js +13 -3
  26. package/dist/__generated__/Precart_license.graphql.d.ts +4 -1
  27. package/dist/__generated__/Precart_license.graphql.js +19 -9
  28. package/dist/__generated__/ServerConfigProvider_viewer.graphql.d.ts +23 -0
  29. package/dist/__generated__/ServerConfigProvider_viewer.graphql.js +57 -0
  30. package/dist/__generated__/StoreModalCartQuery.graphql.d.ts +1 -1
  31. package/dist/__generated__/StoreModalCartQuery.graphql.js +13 -4
  32. package/dist/__generated__/StoreModalIndexQuery.graphql.d.ts +1 -1
  33. package/dist/__generated__/StoreModalIndexQuery.graphql.js +5 -5
  34. package/dist/__generated__/StoreModalProductLicense_license.graphql.d.ts +4 -1
  35. package/dist/__generated__/StoreModalProductLicense_license.graphql.js +11 -2
  36. package/dist/__generated__/StoreModalProductQuery.graphql.d.ts +1 -1
  37. package/dist/__generated__/StoreModalProductQuery.graphql.js +12 -3
  38. package/dist/__generated__/StoreModalProductRefetchQuery.graphql.d.ts +1 -1
  39. package/dist/__generated__/StoreModalProductRefetchQuery.graphql.js +42 -32
  40. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.d.ts +1 -1
  41. package/dist/__generated__/TestFontsFormUpdateCustomerMutation.graphql.js +13 -4
  42. package/dist/__generated__/TestModeBanner_viewer.graphql.d.ts +19 -0
  43. package/dist/__generated__/TestModeBanner_viewer.graphql.js +36 -0
  44. package/dist/__generated__/ThemeConfig_viewer.graphql.d.ts +19 -0
  45. package/dist/__generated__/ThemeConfig_viewer.graphql.js +36 -0
  46. package/dist/__tests__/licenseExclusions.test.js +161 -0
  47. package/dist/components/BuyingOptions/index.d.ts +9 -0
  48. package/dist/components/Cart/CartItem/CartItemAdditionalLicenses.js +11 -5
  49. package/dist/components/CookieNotification/index.d.ts +13 -0
  50. package/dist/components/FontdueContextProvider/index.d.ts +17 -0
  51. package/dist/components/FontdueContextProvider/index.js +108 -0
  52. package/dist/components/FontdueContextProvider/index.server.d.ts +4 -0
  53. package/dist/components/FontdueContextProvider/index.server.js +7 -0
  54. package/dist/components/Precart/index.js +12 -7
  55. package/dist/components/StoreModal/StoreModalIndex.js +1 -1
  56. package/dist/components/StoreModalProductLicenseSelection/StoreModalProductLicense.js +10 -4
  57. package/dist/components/TypeTester/TypeTesterStandalone.preload.d.ts +3 -3
  58. package/dist/components/TypeTester/TypeTesterStandalone.preload.js +10 -9
  59. package/dist/config.d.ts +2 -1
  60. package/dist/config.js +17 -6
  61. package/dist/hooks/useResizeObserver.d.ts +11 -0
  62. package/dist/hooks/useResizeObserver.js +23 -0
  63. package/dist/index.d.ts +2 -2
  64. package/dist/index.js +1 -1
  65. package/dist/loadFontdueProviderQuery.d.ts +3 -0
  66. package/dist/loadFontdueProviderQuery.js +10 -0
  67. package/dist/reducer.d.ts +2 -0
  68. package/dist/reducer.js +3 -1
  69. package/dist/vite.d.ts +2 -0
  70. package/dist/vite.js +139 -0
  71. package/package.json +1 -1
  72. package/types/font-face-set.d.ts +11 -0
  73. package/.nvmrc +0 -1
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { Store } from 'redux';
3
+ import { Config } from '../ConfigContext.js';
4
+ import { FontdueAction, FontdueState } from '../../reducer.js';
5
+ import { Components } from '../ComponentsContext.js';
6
+ export declare function EnsureFontdueContext({ children }: {
7
+ children: React.ReactNode;
8
+ }): React.JSX.Element;
9
+ export interface FontdueContextProvider_props {
10
+ children?: React.ReactNode;
11
+ url?: string;
12
+ stripeIntegration?: 'card-element' | 'dynamic';
13
+ config?: Config;
14
+ components?: Components;
15
+ store?: Store<FontdueState, FontdueAction>;
16
+ }
17
+ export default function FontdueContextProvider({ children, url, stripeIntegration, config, components, store, }: FontdueContextProvider_props): React.JSX.Element;
@@ -0,0 +1,108 @@
1
+ 'use client';
2
+
3
+ import React, { Suspense, createContext, useContext, useRef } from 'react';
4
+ import { Provider } from 'react-redux';
5
+ import { RelayEnvironmentProvider } from 'react-relay';
6
+ import { useCurrentEnvironment } from '../../relay/environment.js';
7
+ import ConfigContext, { makeConfig } from '../ConfigContext.js';
8
+ import { createDefaultStore } from '../../reducer.js';
9
+ import ComponentsContext from '../ComponentsContext.js';
10
+ import UrlContext from '../UrlContext.js';
11
+ import { setCorsModalEnabled } from '../../corsError.js';
12
+
13
+ // Marker context used by self-wrapping components to detect a parent
14
+ // FontdueContextProvider/FontdueProvider. When the marker is `true`, a
15
+ // self-wrapping component skips its inner FontdueContextProvider so the
16
+ // outer config/url/store/relay-env flow through. Internal — do not export
17
+ // from the package.
18
+ const FontdueContextMarker = /*#__PURE__*/createContext(false);
19
+
20
+ // Wraps `children` in `FontdueContextProvider` only when no parent
21
+ // FontdueContextProvider/FontdueProvider is present. Self-wrapping components
22
+ // (`<TypeTester>`, `<StoreModal>`, etc.) use this so they Just Work as
23
+ // standalone islands while staying lightweight inside an outer
24
+ // `<FontdueProvider>` tree.
25
+ export function EnsureFontdueContext(_ref) {
26
+ let {
27
+ children
28
+ } = _ref;
29
+ const isInsideContext = useContext(FontdueContextMarker);
30
+ if (isInsideContext) return /*#__PURE__*/React.createElement(React.Fragment, null, children);
31
+ return /*#__PURE__*/React.createElement(FontdueContextProvider, null, children);
32
+ }
33
+ const IS_SERVER = typeof window === typeof undefined;
34
+
35
+ // === Multi-island state-sharing contract ===
36
+ //
37
+ // Frameworks that hydrate per-island (Astro, Vike, Qwik) mount one
38
+ // `FontdueContextProvider` per fontdue-js component on the page. Each island
39
+ // runs its own React reconciler and gets its own copy of every context, so
40
+ // state that lives inside the component tree is duplicated — adding to the
41
+ // cart in one island wouldn't update the CartButton in a different island.
42
+ //
43
+ // Any stateful client to be shared *across islands* (a Redux store, a Relay
44
+ // Environment, or any future cache) must be a module-level singleton on the
45
+ // client and per-render on the server. SSR has the opposite constraint: a
46
+ // client-shared singleton would leak state between concurrent requests.
47
+ //
48
+ // The pattern (see also `relay/environment.ts → useCurrentEnvironment`):
49
+ //
50
+ // let clientThing: Thing | null = null;
51
+ // function useSharedThing() {
52
+ // if (IS_SERVER) return createThing(); // per-render, no leakage
53
+ // if (clientThing == null) clientThing = createThing();
54
+ // return clientThing; // shared across islands
55
+ // }
56
+ //
57
+ // If you add a new context provider here that holds mutable client state,
58
+ // follow this pattern. If it's purely deriving from props (no state), a
59
+ // vanilla `Context.Provider` is fine — each island will compute the same
60
+ // value.
61
+ let clientStore = null;
62
+ function useSharedStore(providedStore, config) {
63
+ const ref = useRef(null);
64
+ if (providedStore) return providedStore;
65
+ if (IS_SERVER) {
66
+ if (ref.current == null) ref.current = createDefaultStore(config);
67
+ return ref.current;
68
+ }
69
+ if (clientStore == null) clientStore = createDefaultStore(config);
70
+ return clientStore;
71
+ }
72
+ // Lightweight wrapper that sets up Fontdue's React contexts (Relay env,
73
+ // Redux store, config, url, components) without rendering any aux UI.
74
+ // `*Preloaded` self-wrapping components use this so they can stand alone
75
+ // without claiming the page-level aux UI slot. Pages that need aux UI
76
+ // (theme/test-mode/consent/tracking) wrap with `FontdueProvider` instead.
77
+ export default function FontdueContextProvider(_ref2) {
78
+ let {
79
+ children,
80
+ url,
81
+ stripeIntegration,
82
+ config,
83
+ components,
84
+ store
85
+ } = _ref2;
86
+ const environment = useCurrentEnvironment({
87
+ url,
88
+ stripeIntegration
89
+ });
90
+ const configValue = makeConfig(config);
91
+ setCorsModalEnabled(configValue.corsErrorModal);
92
+ const sharedStore = useSharedStore(store, config);
93
+ return /*#__PURE__*/React.createElement(FontdueContextMarker.Provider, {
94
+ value: true
95
+ }, /*#__PURE__*/React.createElement(RelayEnvironmentProvider, {
96
+ environment: environment
97
+ }, /*#__PURE__*/React.createElement(Provider, {
98
+ store: sharedStore
99
+ }, /*#__PURE__*/React.createElement(Suspense, {
100
+ fallback: null
101
+ }, /*#__PURE__*/React.createElement(ConfigContext.Provider, {
102
+ value: configValue
103
+ }, /*#__PURE__*/React.createElement(UrlContext.Provider, {
104
+ value: url ?? (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_FONTDUE_URL : undefined) ?? ''
105
+ }, /*#__PURE__*/React.createElement(ComponentsContext.Provider, {
106
+ value: components ?? {}
107
+ }, children)))))));
108
+ }
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import type { FontdueContextProvider_props } from './index.js';
3
+ export type { FontdueContextProvider_props } from './index.js';
4
+ export default function FontdueContextProviderServer(props: FontdueContextProvider_props): Promise<React.JSX.Element>;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import FontdueContextProvider from './index.js';
3
+ // RSC entry. The client component does the work; this exists so the
4
+ // `react-server` export condition resolves to a server-renderable file.
5
+ export default async function FontdueContextProviderServer(props) {
6
+ return /*#__PURE__*/React.createElement(FontdueContextProvider, props);
7
+ }
@@ -81,7 +81,7 @@ const specFromLicense = license => {
81
81
  }
82
82
  return licenseOptionSpec;
83
83
  };
84
- _Precart_license2.default.hash && _Precart_license2.default.hash !== "0a642f10f28ca7d3857db23cf645895a" && console.error("The definition of 'Precart_license' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _Precart_license2.default;
84
+ _Precart_license2.default.hash && _Precart_license2.default.hash !== "db49280bb01f8bbef6283c0d3fba99e1" && console.error("The definition of 'Precart_license' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _Precart_license2.default;
85
85
  function Precart(_ref3) {
86
86
  var _viewer$precart2, _collection$licenses3;
87
87
  let {
@@ -120,7 +120,7 @@ function Precart(_ref3) {
120
120
  });
121
121
  }, [environment]);
122
122
  const onSelectLicense = (id, checked) => {
123
- var _collection$licenses;
123
+ var _collection$licenses, _license$excludedLice;
124
124
  const license = collection === null || collection === void 0 ? void 0 : (_collection$licenses = collection.licenses) === null || _collection$licenses === void 0 ? void 0 : _collection$licenses.find(node => node.id === id);
125
125
  if (!license) return;
126
126
  const licenseOptionSpec = specFromLicense(license);
@@ -128,7 +128,8 @@ function Precart(_ref3) {
128
128
  type: 'PRECART_TOGGLE_LICENSE',
129
129
  licenseId: id,
130
130
  checked,
131
- licenseOptionSpec
131
+ licenseOptionSpec,
132
+ excludedLicenseIds: (_license$excludedLice = license.excludedLicenses) === null || _license$excludedLice === void 0 ? void 0 : _license$excludedLice.map(l => l.id)
132
133
  });
133
134
  };
134
135
  const onSelectLicenseOption = (licenseId, variableId, optionId) => {
@@ -217,10 +218,14 @@ function Precart(_ref3) {
217
218
  }, [precartOpen]);
218
219
  (0, _react.useEffect)(() => {
219
220
  var _collection$licenses2;
220
- collection === null || collection === void 0 ? void 0 : (_collection$licenses2 = collection.licenses) === null || _collection$licenses2 === void 0 ? void 0 : _collection$licenses2.filter(license => license.defaultSelected).map(specFromLicense).forEach(spec => dispatch({
221
- type: 'PRECART_SELECT_LICENSE',
222
- licenseOptionSpec: spec
223
- }));
221
+ collection === null || collection === void 0 ? void 0 : (_collection$licenses2 = collection.licenses) === null || _collection$licenses2 === void 0 ? void 0 : _collection$licenses2.filter(license => license.defaultSelected).forEach(license => {
222
+ var _license$excludedLice2;
223
+ return dispatch({
224
+ type: 'PRECART_SELECT_LICENSE',
225
+ licenseOptionSpec: specFromLicense(license),
226
+ excludedLicenseIds: (_license$excludedLice2 = license.excludedLicenses) === null || _license$excludedLice2 === void 0 ? void 0 : _license$excludedLice2.map(l => l.id)
227
+ });
228
+ });
224
229
  }, []);
225
230
  function renderConfirmingModal(subtotal) {
226
231
  var _viewer$precart3, _viewer$precart3$skus;
@@ -15,7 +15,7 @@ const StoreModalIndex = _ref => {
15
15
  let {
16
16
  prepared
17
17
  } = _ref;
18
- const data = (0, _reactRelay.usePreloadedQuery)((_StoreModalIndexQuery2.default.hash && _StoreModalIndexQuery2.default.hash !== "300b462c41a951b4f94c4b8da7a6821f" && console.error("The definition of 'StoreModalIndexQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalIndexQuery2.default), prepared.indexQuery);
18
+ const data = (0, _reactRelay.usePreloadedQuery)((_StoreModalIndexQuery2.default.hash && _StoreModalIndexQuery2.default.hash !== "099c77808b41054885365ebe3eda03c8" && console.error("The definition of 'StoreModalIndexQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalIndexQuery2.default), prepared.indexQuery);
19
19
  const viewer = data.viewer;
20
20
  const edges = (viewer === null || viewer === void 0 ? void 0 : (_viewer$rootCollectio = viewer.rootCollections) === null || _viewer$rootCollectio === void 0 ? void 0 : _viewer$rootCollectio.edges) ?? [];
21
21
  return /*#__PURE__*/_react.default.createElement(_StoreModalPageContainer.default, null, {
@@ -42,7 +42,7 @@ function StoreModalProduceLicense(_ref) {
42
42
  let {
43
43
  license: licenseKey
44
44
  } = _ref;
45
- const license = (0, _reactRelay.useFragment)((_StoreModalProductLicense_license2.default.hash && _StoreModalProductLicense_license2.default.hash !== "5729815aeedac2b8ea39eaab0a986b48" && console.error("The definition of 'StoreModalProductLicense_license' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductLicense_license2.default), licenseKey);
45
+ const license = (0, _reactRelay.useFragment)((_StoreModalProductLicense_license2.default.hash && _StoreModalProductLicense_license2.default.hash !== "87cd41e76dcd8952d7096940a383b9f3" && console.error("The definition of 'StoreModalProductLicense_license' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalProductLicense_license2.default), licenseKey);
46
46
  const licenseOptions = (0, _reactRedux.useSelector)(state => state.licenseOptions);
47
47
  const selectedOptions = licenseOptions.find(_ref2 => {
48
48
  let {
@@ -53,11 +53,13 @@ function StoreModalProduceLicense(_ref) {
53
53
  const isSelected = Boolean(selectedOptions);
54
54
  const dispatch = (0, _reactRedux.useDispatch)();
55
55
  const handleSelectLicense = (0, _react.useCallback)(() => {
56
+ var _license$excludedLice;
56
57
  dispatch({
57
58
  type: 'PRECART_TOGGLE_LICENSE',
58
59
  licenseId: license.id,
59
60
  checked: !isSelected,
60
- licenseOptionSpec: specFromLicense(license)
61
+ licenseOptionSpec: specFromLicense(license),
62
+ excludedLicenseIds: (_license$excludedLice = license.excludedLicenses) === null || _license$excludedLice === void 0 ? void 0 : _license$excludedLice.map(l => l.id)
61
63
  });
62
64
  }, [dispatch, license, isSelected]);
63
65
  const handleLicenseOptionChange = (0, _react.useCallback)(_ref3 => {
@@ -66,9 +68,11 @@ function StoreModalProduceLicense(_ref) {
66
68
  optionId
67
69
  } = _ref3;
68
70
  if (!isSelected) {
71
+ var _license$excludedLice2;
69
72
  dispatch({
70
73
  type: 'PRECART_SELECT_LICENSE',
71
- licenseOptionSpec: specFromLicense(license)
74
+ licenseOptionSpec: specFromLicense(license),
75
+ excludedLicenseIds: (_license$excludedLice2 = license.excludedLicenses) === null || _license$excludedLice2 === void 0 ? void 0 : _license$excludedLice2.map(l => l.id)
72
76
  });
73
77
  }
74
78
  dispatch({
@@ -80,9 +84,11 @@ function StoreModalProduceLicense(_ref) {
80
84
  }, [dispatch, license, isSelected]);
81
85
  (0, _react.useEffect)(() => {
82
86
  if (license.defaultSelected) {
87
+ var _license$excludedLice3;
83
88
  dispatch({
84
89
  type: 'PRECART_SELECT_LICENSE',
85
- licenseOptionSpec: specFromLicense(license)
90
+ licenseOptionSpec: specFromLicense(license),
91
+ excludedLicenseIds: (_license$excludedLice3 = license.excludedLicenses) === null || _license$excludedLice3 === void 0 ? void 0 : _license$excludedLice3.map(l => l.id)
86
92
  });
87
93
  }
88
94
  }, []);
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
- import { LoadSerializableQueryOptions, SerializablePreloadedQuery } from '../../relay/loadSerializableQuery';
3
- import { TypeTesterStandaloneQuery } from '../../__generated__/TypeTesterStandaloneQuery.graphql';
4
- import { TypeTesterStandalonePreloadedQueryRenderer } from './TypeTesterStandalone';
2
+ import { LoadSerializableQueryOptions, SerializablePreloadedQuery } from '../../relay/loadSerializableQuery.js';
3
+ import { TypeTesterStandaloneQuery } from '../../__generated__/TypeTesterStandaloneQuery.graphql.js';
4
+ import { TypeTesterStandalonePreloadedQueryRenderer } from './TypeTesterStandalone.js';
5
5
  export type TypeTesterPreloadedQuery = SerializablePreloadedQuery<TypeTesterStandaloneQuery>;
6
6
  export interface LoadTypeTesterQueryVariables {
7
7
  familyName: string;
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import loadSerializableQuery from '../../relay/loadSerializableQuery';
3
- import TypeTesterStandaloneQueryNode from '../../__generated__/TypeTesterStandaloneQuery.graphql';
4
- import FontdueProvider from '../FontdueProvider';
5
- import { TypeTesterStandalonePreloadedQueryRenderer } from './TypeTesterStandalone';
2
+ import loadSerializableQuery from '../../relay/loadSerializableQuery.js';
3
+ import TypeTesterStandaloneQueryNode from '../../__generated__/TypeTesterStandaloneQuery.graphql.js';
4
+ import FontdueContextProvider from '../FontdueContextProvider/index.js';
5
+ import { TypeTesterStandalonePreloadedQueryRenderer } from './TypeTesterStandalone.js';
6
6
  export async function loadTypeTesterQuery(variables, options) {
7
7
  return loadSerializableQuery(TypeTesterStandaloneQueryNode, {
8
8
  familyName: variables.familyName,
@@ -10,10 +10,11 @@ export async function loadTypeTesterQuery(variables, options) {
10
10
  selectable: variables.selectable ?? true
11
11
  }, options);
12
12
  }
13
- // Self-wraps with FontdueProvider so consumers don't need to. The provider
14
- // state is a module-level singleton under the hood, so multiple
15
- // TypeTesterPreloaded (or other *Preloaded) components on a page all share
16
- // one Relay env + Redux store + auxiliary UI.
13
+ // Self-wraps with FontdueContextProvider so consumers don't need to wire up
14
+ // Relay env / Redux store / contexts at the call site. Aux UI (theme,
15
+ // test-mode banner, consent, tracking) is NOT rendered here that belongs
16
+ // to a single layout-level `<FontdueProvider>` per page. State is shared
17
+ // across multiple preloaded components on a page via module-level singletons.
17
18
  export function TypeTesterPreloaded(props) {
18
- return /*#__PURE__*/React.createElement(FontdueProvider, null, /*#__PURE__*/React.createElement(TypeTesterStandalonePreloadedQueryRenderer, props));
19
+ return /*#__PURE__*/React.createElement(FontdueContextProvider, null, /*#__PURE__*/React.createElement(TypeTesterStandalonePreloadedQueryRenderer, props));
19
20
  }
package/dist/config.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export interface FontdueConfig {
1
+ import type { Config } from './components/ConfigContext.js';
2
+ export interface FontdueConfig extends Config {
2
3
  url?: string;
3
4
  stripeIntegration?: 'card-element' | 'dynamic';
4
5
  }
package/dist/config.js CHANGED
@@ -1,14 +1,25 @@
1
1
  // Module-level global configuration. Consumers call `configure()` once at
2
- // app startup (e.g. Astro middleware, Next layout, Vite entry) and every
3
- // fontdue-js entry point reads defaults from here. Explicit options passed
4
- // to loadSerializableQuery / FontdueProvider still override.
2
+ // app startup (Astro layout, Next layout, Vite entry) and every fontdue-js
3
+ // entry point reads defaults from here.
4
+ //
5
+ // Precedence (low to high):
6
+ // 1. Server admin config (from the tenant dashboard, fetched via Relay)
7
+ // 2. configure({...}) — module-level code defaults
8
+ // 3. <FontdueProvider config={...}> — per-instance prop override
5
9
  //
6
10
  // Per-request isolation note: `configure` sets module-level state. That's
7
- // fine for single-tenant apps (one URL for the whole site). Multi-tenant
8
- // servers that switch URL per request should pass `url` explicitly to
9
- // loadSerializableQuery rather than rely on configure().
11
+ // fine for single-tenant apps. Multi-tenant servers that switch URL per
12
+ // request should pass `url` explicitly to loadSerializableQuery rather than
13
+ // rely on configure().
14
+
15
+ // Network-layer + UI config in one bag. UI fields come from `Config` (the
16
+ // shape passed to ConfigContext); URL + stripeIntegration are network-layer.
10
17
 
11
18
  let current = {};
19
+
20
+ // Shallow-merges with prior values. Calling `configure({tracking: {...}})`
21
+ // replaces the whole `tracking` subtree; pass nested fields fully populated
22
+ // when partial-updating.
12
23
  export function configure(opts) {
13
24
  current = {
14
25
  ...current,
@@ -0,0 +1,11 @@
1
+ type ResizeObserverCtor = new (callback: ResizeObserverCallback) => {
2
+ observe: (target: Element) => void;
3
+ unobserve?: (target: Element) => void;
4
+ disconnect?: () => void;
5
+ };
6
+ export type UseResizeObserverOptions = {
7
+ polyfill?: ResizeObserverCtor | null;
8
+ };
9
+ export type UseResizeObserverCallback = (entry: ResizeObserverEntry, observer: ResizeObserver) => void;
10
+ export default function useResizeObserver<T extends Element>(target: React.RefObject<T | null> | T | null, callback: UseResizeObserverCallback, options?: UseResizeObserverOptions): void;
11
+ export {};
@@ -0,0 +1,23 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ // Loose constructor type so the SSR no-op polyfills used as fallbacks (which
4
+ // don't implement `disconnect`) still type-check.
5
+
6
+ export default function useResizeObserver(target, callback) {
7
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
8
+ const callbackRef = useRef(callback);
9
+ callbackRef.current = callback;
10
+ useEffect(() => {
11
+ const element = target && 'current' in target ? target.current : target;
12
+ if (!element) return;
13
+ const Ctor = options.polyfill ?? (typeof ResizeObserver !== 'undefined' ? ResizeObserver : null);
14
+ if (!Ctor) return;
15
+ const observer = new Ctor(entries => {
16
+ for (const entry of entries) callbackRef.current(entry, observer);
17
+ });
18
+ observer.observe(element);
19
+ return () => {
20
+ if (observer.disconnect) observer.disconnect();else if (observer.unobserve) observer.unobserve(element);
21
+ };
22
+ }, [target, options.polyfill]);
23
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { configure, getConfig } from './config';
2
- export type { FontdueConfig } from './config';
1
+ export { default as loadFontdueProviderQuery } from './loadFontdueProviderQuery.js';
2
+ export type { FontdueProviderPreloadedQuery } from './loadFontdueProviderQuery.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { configure, getConfig } from './config';
1
+ export { default as loadFontdueProviderQuery } from './loadFontdueProviderQuery.js';
@@ -0,0 +1,3 @@
1
+ import type { FontdueProviderPreloadedQuery } from './components/FontdueProvider/index.js';
2
+ export type { FontdueProviderPreloadedQuery };
3
+ export default function loadFontdueProviderQuery(): Promise<FontdueProviderPreloadedQuery>;
@@ -0,0 +1,10 @@
1
+ import loadSerializableQuery from './relay/loadSerializableQuery.js';
2
+ import FontdueProviderQueryNode from './__generated__/FontdueProviderQuery.graphql.js';
3
+ // Fetches the data aux UI (theme custom properties, test-mode flag) needs to
4
+ // render synchronously on the server and on hydration. Pass the result to
5
+ // `<FontdueProvider preloadedQuery={…}>` in your layout. Without it, aux UI
6
+ // falls back to lazy-fetching on first client render — which causes a visible
7
+ // theming flash because `ThemeConfig` injects CSS variables.
8
+ export default async function loadFontdueProviderQuery() {
9
+ return loadSerializableQuery(FontdueProviderQueryNode, {});
10
+ }
package/dist/reducer.d.ts CHANGED
@@ -57,10 +57,12 @@ export type FontdueAction = {
57
57
  type: 'PRECART_TOGGLE_LICENSE';
58
58
  licenseId: string;
59
59
  licenseOptionSpec: LicenseOptionSpec;
60
+ excludedLicenseIds?: readonly string[];
60
61
  checked: boolean;
61
62
  } | {
62
63
  type: 'PRECART_SELECT_LICENSE';
63
64
  licenseOptionSpec: LicenseOptionSpec;
65
+ excludedLicenseIds?: readonly string[];
64
66
  } | {
65
67
  type: 'PRECART_SELECT_LICENSE_OPTION';
66
68
  licenseId: string;
package/dist/reducer.js CHANGED
@@ -34,9 +34,11 @@ const selectLicense = (state, action) => {
34
34
  if (!(action.type === 'PRECART_SELECT_LICENSE' || action.type === 'PRECART_TOGGLE_LICENSE')) return state;
35
35
  const existingLicenseOption = state.licenseOptions.findIndex(option => option.licenseId === action.licenseOptionSpec.licenseId);
36
36
  if (existingLicenseOption >= 0) return state;
37
+ const excludedIds = action.excludedLicenseIds ?? [];
38
+ const filtered = excludedIds.length > 0 ? state.licenseOptions.filter(option => !excludedIds.includes(option.licenseId)) : state.licenseOptions;
37
39
  return {
38
40
  ...state,
39
- licenseOptions: state.licenseOptions.concat(action.licenseOptionSpec)
41
+ licenseOptions: filtered.concat(action.licenseOptionSpec)
40
42
  };
41
43
  };
42
44
  const toggleLicense = (state, action) => {
package/dist/vite.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { PluginOption } from 'vite';
2
+ export default function fontdueJs(): PluginOption[];
package/dist/vite.js ADDED
@@ -0,0 +1,139 @@
1
+ // Vite plugin that fixes the SSR-side CJS interop fontdue-js's deps need.
2
+ //
3
+ // fontdue-js itself is published as transpiled (not bundled) ESM. Its Relay
4
+ // + draft-js + fbjs deps still ship as CJS using `module.exports = require(…)`
5
+ // re-export shapes that Node's strict ESM-CJS interop (and Vite's dev SSR
6
+ // module-runner) can't named-import from. Without help, `import { graphql }
7
+ // from 'react-relay'` blows up at SSR time with `Named export 'graphql' not
8
+ // found` or `module is not defined`.
9
+ //
10
+ // `vite-plugin-cjs-interop` is the canonical fix for this — referenced by the
11
+ // Relay maintainers in https://github.com/facebook/relay/issues/4548. It
12
+ // rewrites named imports of the listed packages to read from `.default` after
13
+ // CJS load, which is what Node + Vite actually expose.
14
+ //
15
+ // Consumers wire it up like:
16
+ //
17
+ // // vite.config.ts
18
+ // import fontdueJs from 'fontdue-js/vite';
19
+ // export default defineConfig({ plugins: [fontdueJs()] });
20
+ //
21
+ // Returned shape is structurally a Vite Plugin (or array of plugins).
22
+ //
23
+ // @ts-ignore — vite-plugin-cjs-interop has no bundled types but exports
24
+ // cjsInterop at runtime.
25
+ import { cjsInterop } from 'vite-plugin-cjs-interop';
26
+ import { createRequire } from 'node:module';
27
+
28
+ // Packages whose imports cjsInterop should rewrite to default-import +
29
+ // destructure shape. Limit this to packages that genuinely ship as CJS-only
30
+ // with `module.exports = require('./lib')` shapes that defeat named-import
31
+ // resolution. React-redux is real ESM and must NOT be in this list — its
32
+ // ESM build has true named exports.
33
+ const CJS_DEPS = ['react-relay', 'relay-runtime', 'draft-js', 'fbjs'];
34
+
35
+ // Packages to pre-bundle for the browser via Vite's optimizer. Pre-bundling
36
+ // pulls a package's transitive deps into one esbuild output, which sidesteps
37
+ // the cjs-module-lexer trap when an ESM package (react-redux) imports a CJS
38
+ // transitive (use-sync-external-store). Without this, the browser dev path
39
+ // fails with "does not provide an export named useSyncExternalStoreWithSelector".
40
+ //
41
+ // `react-google-recaptcha` and `@stripe/react-stripe-js` are pulled into the
42
+ // StoreModal subtree (checkout flow). Both transitively import `prop-types`,
43
+ // whose `module.exports = require('./factoryWithTypeCheckers')(...)` shape
44
+ // hides exports behind a runtime call — cjs-module-lexer can't see them, so
45
+ // the optimizer's auto-generated wrapper omits a `default` export. Including
46
+ // the consumers + setting `needsInterop` on `prop-types` forces Vite to
47
+ // synthesize a default export so `import PropTypes from 'prop-types'` resolves.
48
+ const CLIENT_OPTIMIZE = [...CJS_DEPS, 'react-redux', 'react-google-recaptcha', '@stripe/react-stripe-js', 'prop-types',
49
+ // Transitive deps that Vite would otherwise discover late, after fontdue-js
50
+ // subpaths are first imported. Each late discovery triggers a re-optimize
51
+ // and a new browserHash for `react`, which produces the dual-React bug
52
+ // described above `getFontdueJsSubpaths`. Pinning them here keeps the
53
+ // initial scan complete.
54
+ '@sentry/react', '@stripe/stripe-js', 'react-error-boundary', 'redux'];
55
+ const NEEDS_INTEROP = ['prop-types'];
56
+
57
+ // Every public `fontdue-js/*` entry from package.json `exports`. Pre-including
58
+ // them in `optimizeDeps` forces Vite to scan them on the first dev startup
59
+ // instead of discovering them later when a route lazy-loads.
60
+ //
61
+ // Why this matters: when Vite finds a new dep mid-session it re-runs the
62
+ // optimizer, which mints a new `?v=` browserHash for every dep chunk. Chunks
63
+ // already loaded in the page keep their old `?v=` baked into their imports,
64
+ // so a freshly-fetched chunk that imports `react?v=NEW` ends up coexisting
65
+ // with the previously-loaded `react?v=OLD`. Two physical React modules in
66
+ // the same page → two `ReactCurrentDispatcher`s → `useContext` returns null
67
+ // → "Invalid hook call" on first navigation to an uncached route.
68
+ //
69
+ // Frameworks that lazy-load modules (RR7's route splitter, Astro's per-island
70
+ // hydration) make this near-certain unless the optimizer sees the full set
71
+ // up front. Vite's glob form (`fontdue-js/*`) walks the filesystem and gets
72
+ // confused by our `exports`-mapped layout (the dist files don't sit at the
73
+ // subpath names), so we read `exports` directly — that's the authoritative
74
+ // list.
75
+ function getFontdueJsSubpaths() {
76
+ const require = createRequire(import.meta.url);
77
+ const pkg = require('../package.json');
78
+ const out = [];
79
+ for (const key of Object.keys(pkg.exports ?? {})) {
80
+ if (!key.startsWith('./')) continue;
81
+ if (key.endsWith('.css')) continue;
82
+ if (key === './vite') continue;
83
+ out.push(`fontdue-js/${key.slice(2)}`);
84
+ }
85
+ return out;
86
+ }
87
+ export default function fontdueJs() {
88
+ const fontdueSubpaths = getFontdueJsSubpaths();
89
+ return [cjsInterop({
90
+ dependencies: CJS_DEPS
91
+ }), {
92
+ name: 'fontdue-js',
93
+ config() {
94
+ return {
95
+ // Pre-bundle the CJS-shaped deps via esbuild for the client
96
+ // bundle. Without this, Astro dev's browser pass tries to
97
+ // ESM-import `Environment` from `relay-runtime` and fails —
98
+ // its `module.exports = require('./lib/index.js')` shape can't
99
+ // be statically analyzed for named exports.
100
+ optimizeDeps: {
101
+ include: [...fontdueSubpaths, ...CLIENT_OPTIMIZE],
102
+ needsInterop: [...NEEDS_INTEROP]
103
+ },
104
+ // fbjs (transitive via draft-js) references Node's `global` at
105
+ // module init. Browsers don't define it, so the pre-bundled chunk
106
+ // throws `ReferenceError: global is not defined`. Aliasing
107
+ // `global` to `globalThis` at compile time covers both browser
108
+ // and SSR paths.
109
+ define: {
110
+ global: 'globalThis'
111
+ }
112
+ };
113
+ },
114
+ // Apply `noExternal` to every server-side Vite environment so
115
+ // fontdue-js source flows through Vite's transform pipeline and
116
+ // cjsInterop can rewrite its imports of CJS-shaped Relay/draft-js/
117
+ // fbjs. The legacy `ssr.noExternal` only covered the default `ssr`
118
+ // environment, so Astro 6's separate `prerender` build externalized
119
+ // fontdue-js and `import { graphql } from 'react-relay'` ran against
120
+ // unrewritten CJS at prerender time.
121
+ //
122
+ // `config.consumer` would be the principled check, but at
123
+ // `configEnvironment` time Vite hasn't populated it yet (undefined
124
+ // for ssr/prerender/client alike). Match by name instead:
125
+ // - 'ssr': default Vite SSR env (plain Vite SSR, RR7)
126
+ // - 'prerender': Astro 6's static-prerender env
127
+ // The `consumer` fallback is kept as future-proofing if a framework
128
+ // ever names its server env something else but does populate consumer.
129
+ configEnvironment(name, config) {
130
+ if (name === 'ssr' || name === 'prerender' || config.consumer === 'server') {
131
+ return {
132
+ resolve: {
133
+ noExternal: ['fontdue-js']
134
+ }
135
+ };
136
+ }
137
+ }
138
+ }];
139
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fontdue-js",
3
- "version": "2.22.3",
3
+ "version": "2.23.0",
4
4
  "scripts": {
5
5
  "build": "npm run relay && run-p build-js build-css build-ts",
6
6
  "build-js": "babel src --out-dir dist --extensions .ts,.tsx,.js,.jsx",
@@ -0,0 +1,11 @@
1
+ declare global {
2
+ interface FontFaceSet {
3
+ add(font: FontFace): FontFaceSet;
4
+ delete(font: FontFace): boolean;
5
+ clear(): void;
6
+ has(font: FontFace): boolean;
7
+ readonly size: number;
8
+ }
9
+ }
10
+
11
+ export {};
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- v14.13.1