@revenuecat/purchases-ui-js 4.8.20 → 4.8.22

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.
@@ -24,6 +24,7 @@
24
24
  import { onMount } from "svelte";
25
25
  import {
26
26
  clamp,
27
+ getDefaultPageIndex,
27
28
  getSwipeTargetPage,
28
29
  getTranslation,
29
30
  mapPageAlignment,
@@ -132,10 +133,6 @@
132
133
  let pageWidth = $derived(carouselWidth - pageOffset);
133
134
  let gestureStartPage = $state<number | null>(null);
134
135
 
135
- function getDefaultPageIndex() {
136
- return pages.at(initial_page_index) !== undefined ? initial_page_index : 0;
137
- }
138
-
139
136
  function normalizePageIndex(index: number) {
140
137
  if (pages.length === 0) {
141
138
  return 0;
@@ -199,7 +196,7 @@
199
196
  }
200
197
 
201
198
  onMount(() => {
202
- const initialPage = getDefaultPageIndex();
199
+ const initialPage = getDefaultPageIndex(pages, initial_page_index);
203
200
  currentPage = initialPage;
204
201
 
205
202
  stopTransition();
@@ -265,7 +262,7 @@
265
262
  destinationIndex: destinationPage,
266
263
  originContextName: getPageContextName(gestureStartPage),
267
264
  destinationContextName: getPageContextName(destinationPage),
268
- defaultIndex: getDefaultPageIndex(),
265
+ defaultIndex: getDefaultPageIndex(pages, initial_page_index),
269
266
  });
270
267
  }
271
268
 
@@ -1,6 +1,12 @@
1
1
  import type { VerticalAlignment } from "../../types/alignment";
2
2
  export declare function mapPageAlignment(alignment: VerticalAlignment): string;
3
3
  export declare function clamp(value: number, min: number, max: number): number;
4
+ /**
5
+ * Resolves the carousel's starting page. Out-of-range indexes (including a
6
+ * missing `pages[i]` hole) fall back to 0. Uses index access rather than
7
+ * `Array.prototype.at` so Chrome < 92 does not throw while mounting.
8
+ */
9
+ export declare function getDefaultPageIndex<T>(pages: readonly T[], initialPageIndex: number): number;
4
10
  /**
5
11
  * Advance one page when the drag passed this fraction of a page's width,
6
12
  * matching the iOS SDK carousel. A shorter drag snaps back to the page the
@@ -11,6 +11,14 @@ export function mapPageAlignment(alignment) {
11
11
  export function clamp(value, min, max) {
12
12
  return Math.max(min, Math.min(value, max));
13
13
  }
14
+ /**
15
+ * Resolves the carousel's starting page. Out-of-range indexes (including a
16
+ * missing `pages[i]` hole) fall back to 0. Uses index access rather than
17
+ * `Array.prototype.at` so Chrome < 92 does not throw while mounting.
18
+ */
19
+ export function getDefaultPageIndex(pages, initialPageIndex) {
20
+ return pages[initialPageIndex] !== undefined ? initialPageIndex : 0;
21
+ }
14
22
  /**
15
23
  * Advance one page when the drag passed this fraction of a page's width,
16
24
  * matching the iOS SDK carousel. A shorter drag snaps back to the page the
@@ -1,12 +1,16 @@
1
1
  <script module lang="ts">
2
2
  import Image from "./Image.svelte";
3
3
  import { componentDecorator } from "../../stories/component-decorator";
4
+ import { localizationDecorator } from "../../stories/localization-decorator";
4
5
  import { defineMeta } from "@storybook/addon-svelte-csf";
5
6
 
6
7
  const { Story } = defineMeta({
7
8
  title: "Components/Image",
8
9
  component: Image,
9
- decorators: [componentDecorator()],
10
+ decorators: [
11
+ componentDecorator(),
12
+ localizationDecorator({ defaultLocale: "en_US", localizations: {} }),
13
+ ],
10
14
  args: {
11
15
  type: "image",
12
16
  id: "image",
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { getColorModeContext } from "../../stores/color-mode";
3
3
  import { useConditionContext } from "../../stores/condition-context.svelte";
4
+ import { getLocalizationContext } from "../../stores/localization";
4
5
  import type { ImageProps } from "../../types/components/image";
5
6
  import {
6
7
  css,
@@ -12,15 +13,19 @@
12
13
  } from "../../utils/base-utils";
13
14
  import { resolveOverrideProperties } from "../../utils/style-utils";
14
15
  import ClipPath from "./ClipPath.svelte";
16
+ import { resolveImageSource } from "./image-utils";
15
17
  import { viewBoxSize } from "./view-box-size";
16
18
  import Overlay from "./Overlay.svelte";
17
19
 
18
20
  const props: ImageProps = $props();
19
21
 
20
22
  const buildConditionContext = useConditionContext({ state: true });
23
+ const { getLocalizedImage } = getLocalizationContext();
24
+
25
+ const conditionContext = $derived(buildConditionContext());
26
+
21
27
  const {
22
28
  id,
23
- source,
24
29
  size,
25
30
  mask_shape,
26
31
  fit_mode,
@@ -33,10 +38,14 @@
33
38
  } = $derived.by(() => {
34
39
  return {
35
40
  ...props,
36
- ...resolveOverrideProperties(buildConditionContext(), props.overrides),
41
+ ...resolveOverrideProperties(conditionContext, props.overrides),
37
42
  };
38
43
  });
39
44
 
45
+ const source = $derived(
46
+ resolveImageSource(props, conditionContext, getLocalizedImage),
47
+ );
48
+
40
49
  const getColorMode = getColorModeContext();
41
50
  const colorMode = $derived(getColorMode());
42
51
  const image = $derived(mapColorMode(colorMode, source));
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { readable, writable } from "svelte/store";
3
3
  import { setColorModeContext } from "../../stores/color-mode";
4
+ import { setLocalizationContext } from "../../stores/localization";
4
5
  import { setPaywallContext } from "../../stores/paywall";
5
6
  import Image from "./Image.svelte";
6
7
  import type { ImageProps } from "../../types/components/image";
@@ -8,6 +9,10 @@
8
9
  const props: ImageProps = $props();
9
10
 
10
11
  setColorModeContext();
12
+ setLocalizationContext(() => ({
13
+ defaultLocale: "en_US",
14
+ localizations: {},
15
+ }));
11
16
  setPaywallContext({
12
17
  selectedPackageId: writable(undefined),
13
18
  variablesPerPackage: readable(undefined),
@@ -0,0 +1,9 @@
1
+ import type { ImageProps } from "../../types/components/image";
2
+ import type { ImageInfo } from "../../types/media";
3
+ import { type ConditionContext } from "../../utils/style-utils";
4
+ import type { GetLocalizedImage } from "../../stores/localization";
5
+ /**
6
+ * Resolves the ImageInfo to render for an image component, preferring
7
+ * a matching override but defaulting to the image source.
8
+ */
9
+ export declare function resolveImageSource(props: ImageProps, context: ConditionContext, getLocalizedImage: GetLocalizedImage): ImageInfo;
@@ -0,0 +1,20 @@
1
+ import { filterMatchingOverrides, } from "../../utils/style-utils";
2
+ /**
3
+ * Resolves the ImageInfo to render for an image component, preferring
4
+ * a matching override but defaulting to the image source.
5
+ */
6
+ export function resolveImageSource(props, context, getLocalizedImage) {
7
+ const resolveLocalizedSource = (source, overrideSourceLid) => overrideSourceLid
8
+ ? (getLocalizedImage(overrideSourceLid) ?? source)
9
+ : source;
10
+ let resolvedOverrideImage;
11
+ for (const override of filterMatchingOverrides(context, props.overrides)) {
12
+ const image = resolveLocalizedSource(override.properties.source, override.properties.override_source_lid);
13
+ if (image) {
14
+ resolvedOverrideImage = image;
15
+ }
16
+ }
17
+ return (resolvedOverrideImage ??
18
+ resolveLocalizedSource(props.source, props.override_source_lid) ??
19
+ props.source);
20
+ }
@@ -30,6 +30,7 @@
30
30
  import { OVERRIDE_PAYWALL } from "./fixtures/override-paywall";
31
31
  import { SHEET_PAYWALL } from "./fixtures/sheet-paywall";
32
32
  import { SHEET_PAYWALL_VIDEO_STACKING } from "./fixtures/sheet-video-stacking-paywall";
33
+ import { GRADIENT_SHEET_PAYWALL } from "./fixtures/gradient-sheet-paywall";
33
34
  import { STACK_PAYWALL } from "./fixtures/stack-paywall";
34
35
  import { VARIABLES } from "./fixtures/variables";
35
36
  import { CUSTOM_VARIABLES_PAYWALL } from "./fixtures/custom-variables-paywall";
@@ -167,6 +168,19 @@
167
168
  }}
168
169
  />
169
170
 
171
+ <Story
172
+ name="Sheet — gradient background"
173
+ decorators={[viewportDecorator(500, 500, 0)]}
174
+ play={async ({ canvasElement }) => {
175
+ const button = canvasElement.querySelector("button");
176
+ button?.click();
177
+ await waitForAnimations();
178
+ }}
179
+ args={{
180
+ paywallData: GRADIENT_SHEET_PAYWALL,
181
+ }}
182
+ />
183
+
170
184
  <Story
171
185
  name="Background - Color"
172
186
  decorators={[viewportDecorator(500, 500, 0)]}
@@ -442,8 +442,9 @@
442
442
  </script>
443
443
 
444
444
  <svelte:boundary onerror={onError}>
445
+ <!-- Outside the root: filter/transform on `.blur` would clip the fixed backdrop to the paywall box. -->
446
+ <ViewportBackdrop model={viewportBackdropModel} blur={!!sheet} />
445
447
  <div class={paywallClass} style={paywallStyle}>
446
- <ViewportBackdrop model={viewportBackdropModel} />
447
448
  {#if header}
448
449
  <div
449
450
  class="header-wrapper"
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { getColorModeContext } from "../../stores/color-mode";
3
3
  import { useConditionContext } from "../../stores/condition-context.svelte";
4
- import { getPaywallContext } from "../../stores/paywall";
4
+ import { getPaywallContext, setPaywallContext } from "../../stores/paywall";
5
5
  import BackgroundVideoSurface from "./BackgroundVideoSurface.svelte";
6
6
  import type { SheetProps } from "../../types/components/sheet";
7
7
  import {
@@ -56,21 +56,34 @@
56
56
  }),
57
57
  );
58
58
 
59
- const { onButtonAction } = getPaywallContext();
59
+ const paywallContext = getPaywallContext();
60
+ const { onButtonAction } = paywallContext;
60
61
 
61
62
  let sheet: HTMLDivElement | undefined;
62
63
  let visible = $state(false);
63
64
 
65
+ const hideSheet = () => {
66
+ visible = false;
67
+ };
68
+
69
+ // navigate_back inside the sheet closes the sheet, not the paywall.
70
+ setPaywallContext({
71
+ ...paywallContext,
72
+ onButtonAction: (action, actionId) => {
73
+ if (action.type === "navigate_back") {
74
+ hideSheet();
75
+ return;
76
+ }
77
+ onButtonAction(action, actionId);
78
+ },
79
+ });
80
+
64
81
  onMount(() => {
65
82
  requestAnimationFrame(() => {
66
83
  visible = true;
67
84
  });
68
85
  });
69
86
 
70
- const hideSheet = () => {
71
- visible = false;
72
- };
73
-
74
87
  const ontransitionend = () => {
75
88
  if (visible) {
76
89
  return;
@@ -6,7 +6,10 @@
6
6
  // through to the safe-area canvas. A position:fixed element that explicitly
7
7
  // fills the viewport — including safe areas — is the only consistent paint
8
8
  // surface for gradients and images on iOS Safari.
9
- const { model }: { model: PaywallRootBackgroundModel } = $props();
9
+ const {
10
+ model,
11
+ blur = false,
12
+ }: { model: PaywallRootBackgroundModel; blur?: boolean } = $props();
10
13
 
11
14
  const backdropStyle = $derived.by((): string => {
12
15
  if (
@@ -46,6 +49,7 @@
46
49
  {#if shouldRenderBackdrop}
47
50
  <div
48
51
  class="viewport-backdrop"
52
+ class:blurred={blur}
49
53
  style={backdropStyle !== "" ? backdropStyle : undefined}
50
54
  >
51
55
  {#if model.kind === "video"}
@@ -77,6 +81,14 @@
77
81
  pointer-events: none;
78
82
  overflow: hidden;
79
83
  isolation: isolate;
84
+ transition:
85
+ filter 0.1s ease-in-out,
86
+ transform 0.1s ease-in-out;
87
+ }
88
+
89
+ .viewport-backdrop.blurred {
90
+ filter: blur(10px) brightness(0.8);
91
+ transform: scale(1.045);
80
92
  }
81
93
 
82
94
  .viewport-backdrop-overlay {
@@ -1,6 +1,7 @@
1
1
  import type { PaywallRootBackgroundModel } from "../../utils/background-utils";
2
2
  type $$ComponentProps = {
3
3
  model: PaywallRootBackgroundModel;
4
+ blur?: boolean;
4
5
  };
5
6
  declare const ViewportBackdrop: import("svelte").Component<$$ComponentProps, {}, "">;
6
7
  type ViewportBackdrop = ReturnType<typeof ViewportBackdrop>;
@@ -0,0 +1,2 @@
1
+ import type { PaywallData } from "../../../types/paywall";
2
+ export declare const GRADIENT_SHEET_PAYWALL: PaywallData;
@@ -0,0 +1,68 @@
1
+ import { createStack, createTextComponent } from "./helpers";
2
+ // Gradient background (painted by ViewportBackdrop) plus a button that opens a sheet.
3
+ export const GRADIENT_SHEET_PAYWALL = {
4
+ id: "gradient-sheet-paywall",
5
+ default_locale: "en_US",
6
+ components_localizations: {
7
+ en_US: { open_sheet: "Open sheet", sheet_title: "Sheet" },
8
+ },
9
+ components_config: {
10
+ base: {
11
+ background: {
12
+ type: "color",
13
+ value: {
14
+ light: {
15
+ type: "linear",
16
+ degrees: 45,
17
+ points: [
18
+ { percent: 0, color: "#010101ff" },
19
+ { percent: 100, color: "#f79e00ff" },
20
+ ],
21
+ },
22
+ },
23
+ },
24
+ stack: createStack({
25
+ id: "root",
26
+ name: "Root",
27
+ components: [
28
+ {
29
+ type: "button",
30
+ id: "open-sheet",
31
+ name: "open-sheet",
32
+ action: {
33
+ type: "navigate_to",
34
+ destination: "sheet",
35
+ sheet: {
36
+ type: "sheet",
37
+ id: "sheet",
38
+ name: "sheet",
39
+ size: { width: { type: "fill" }, height: { type: "fit" } },
40
+ background_blur: true,
41
+ stack: createStack({
42
+ id: "sheet-stack",
43
+ name: "Sheet",
44
+ components: [
45
+ createTextComponent({
46
+ id: "sheet-title",
47
+ textLid: "sheet_title",
48
+ }),
49
+ ],
50
+ }),
51
+ },
52
+ },
53
+ stack: createStack({
54
+ id: "open-sheet-stack",
55
+ name: "open-sheet",
56
+ components: [
57
+ createTextComponent({
58
+ id: "open-sheet-text",
59
+ textLid: "open_sheet",
60
+ }),
61
+ ],
62
+ }),
63
+ },
64
+ ],
65
+ }),
66
+ },
67
+ },
68
+ };
@@ -0,0 +1,2 @@
1
+ import type { PaywallData } from "../../../types/paywall";
2
+ export declare const SHEET_CLOSE_BUTTON_PAYWALL: PaywallData;
@@ -0,0 +1,62 @@
1
+ import { createStack, createTextComponent } from "./helpers";
2
+ function button(id, textLid, action) {
3
+ return {
4
+ type: "button",
5
+ id,
6
+ name: id,
7
+ action,
8
+ stack: createStack({
9
+ id: `${id}-stack`,
10
+ name: id,
11
+ components: [createTextComponent({ id: `${id}-text`, textLid })],
12
+ }),
13
+ };
14
+ }
15
+ // Sheet whose close button uses navigate_back, as the builder configures it.
16
+ export const SHEET_CLOSE_BUTTON_PAYWALL = {
17
+ id: "sheet-close-button-paywall",
18
+ default_locale: "en_US",
19
+ components_localizations: {
20
+ en_US: {
21
+ open_sheet: "Included devices",
22
+ sheet_title: "Devices",
23
+ close_sheet: "Close",
24
+ close_paywall: "Dismiss paywall",
25
+ },
26
+ },
27
+ components_config: {
28
+ base: {
29
+ stack: createStack({
30
+ id: "root",
31
+ name: "Root",
32
+ components: [
33
+ button("close-paywall", "close_paywall", { type: "navigate_back" }),
34
+ button("open-sheet", "open_sheet", {
35
+ type: "navigate_to",
36
+ destination: "sheet",
37
+ sheet: {
38
+ type: "sheet",
39
+ id: "devices-sheet",
40
+ name: "devices_sheet",
41
+ size: { width: { type: "fill" }, height: { type: "fit" } },
42
+ background_blur: true,
43
+ stack: createStack({
44
+ id: "sheet-stack",
45
+ name: "Sheet",
46
+ components: [
47
+ createTextComponent({
48
+ id: "sheet-title",
49
+ textLid: "sheet_title",
50
+ }),
51
+ button("close-sheet", "close_sheet", {
52
+ type: "navigate_back",
53
+ }),
54
+ ],
55
+ }),
56
+ },
57
+ }),
58
+ ],
59
+ }),
60
+ },
61
+ },
62
+ };
@@ -142,7 +142,8 @@
142
142
  for (const [key, declaration] of Object.entries(
143
143
  screen.state_declarations ?? {},
144
144
  )) {
145
- if (!Object.hasOwn(merged, key)) merged[key] = declaration;
145
+ if (!Object.prototype.hasOwnProperty.call(merged, key))
146
+ merged[key] = declaration;
146
147
  }
147
148
  }
148
149
  return merged;
@@ -1,7 +1,10 @@
1
1
  import type { Localizations } from "../types/localization";
2
+ import type { ImageInfo } from "../types/media";
2
3
  type GetLocalizedString = (id?: string) => string | undefined;
4
+ export type GetLocalizedImage = (id?: string) => ImageInfo | undefined;
3
5
  interface LocalizationContext {
4
6
  getLocalizedString: GetLocalizedString;
7
+ getLocalizedImage: GetLocalizedImage;
5
8
  }
6
9
  export interface LocalizationContextProps {
7
10
  defaultLocale: string;
@@ -1,9 +1,9 @@
1
1
  import { getContext, setContext } from "svelte";
2
2
  const key = Symbol("localization");
3
3
  export function setLocalizationContext(props) {
4
- const getLocalizedString = (id) => {
4
+ const getLocalizedValue = (id) => {
5
5
  if (!id) {
6
- return "";
6
+ return undefined;
7
7
  }
8
8
  const { selectedLocale, defaultLocale, localizations } = props();
9
9
  const locale = selectedLocale ?? defaultLocale;
@@ -11,8 +11,20 @@ export function setLocalizationContext(props) {
11
11
  const fallback = localizations[defaultLocale]?.[id];
12
12
  return selected ?? fallback;
13
13
  };
14
+ const getLocalizedString = (id) => {
15
+ if (!id) {
16
+ return "";
17
+ }
18
+ const value = getLocalizedValue(id);
19
+ return typeof value === "string" ? value : undefined;
20
+ };
21
+ const getLocalizedImage = (id) => {
22
+ const value = getLocalizedValue(id);
23
+ return typeof value === "object" ? value : undefined;
24
+ };
14
25
  const context = {
15
26
  getLocalizedString,
27
+ getLocalizedImage,
16
28
  };
17
29
  setContext(key, context);
18
30
  return context;
@@ -34,14 +34,14 @@ export class PaywallStateStore {
34
34
  registerDeclarations(declarations) {
35
35
  let changed = false;
36
36
  for (const [key, declaration] of Object.entries(declarations)) {
37
- if (Object.hasOwn(this.#declaredDefaults, key))
37
+ if (Object.prototype.hasOwnProperty.call(this.#declaredDefaults, key))
38
38
  continue;
39
39
  const normalized = this.#normalizeForType(declaration.default, declaration.type);
40
40
  if (normalized === undefined)
41
41
  continue;
42
42
  this.#declaredDefaults[key] = normalized;
43
43
  this.#declaredTypes[key] = declaration.type;
44
- if (!Object.hasOwn(this.#currentValues, key)) {
44
+ if (!Object.prototype.hasOwnProperty.call(this.#currentValues, key)) {
45
45
  this.#currentValues[key] = normalized;
46
46
  changed = true;
47
47
  }
@@ -1,5 +1,5 @@
1
1
  import type { ImageInfo } from "./media";
2
2
  export type LocalizationValue = string | ImageInfo;
3
- type Localization = Partial<Record<string, string>>;
3
+ type Localization = Partial<Record<string, LocalizationValue>>;
4
4
  export type Localizations = Partial<Record<string, Localization>>;
5
5
  export {};
@@ -80,7 +80,7 @@ export function getSafeAreaFallbackColorHeadStyles(paywallData, hostFallbackColo
80
80
  }
81
81
  function colorAtPercent(sortedPoints, percent) {
82
82
  const firstPoint = sortedPoints[0];
83
- const lastPoint = sortedPoints.at(-1);
83
+ const lastPoint = sortedPoints[sortedPoints.length - 1];
84
84
  if (firstPoint == null || lastPoint == null) {
85
85
  return null;
86
86
  }
@@ -29,6 +29,11 @@ export type ConditionContext = {
29
29
  focus?: boolean;
30
30
  error?: boolean;
31
31
  };
32
+ /**
33
+ * Returns the overrides (in array order) whose conditions all match (AND) the
34
+ * given context, without merging their properties.
35
+ */
36
+ export declare const filterMatchingOverrides: <T extends Component>(context: ConditionContext, overrides?: Overrides<T>) => Overrides<T>;
32
37
  /**
33
38
  * Merges the properties of every override whose conditions all match (AND),
34
39
  * applying them in override-array order (later overrides overwrite earlier ones).
@@ -115,6 +115,15 @@ export function collectPackageIdsInDisplayOrder({ stack, sticky_footer, }) {
115
115
  }
116
116
  return ids;
117
117
  }
118
+ /**
119
+ * Returns the overrides (in array order) whose conditions all match (AND) the
120
+ * given context, without merging their properties.
121
+ */
122
+ export const filterMatchingOverrides = (context, overrides) => {
123
+ if (!overrides)
124
+ return [];
125
+ return overrides.filter((override) => override.conditions.every((c) => conditionMatches(c, context)));
126
+ };
118
127
  /**
119
128
  * Merges the properties of every override whose conditions all match (AND),
120
129
  * applying them in override-array order (later overrides overwrite earlier ones).
@@ -128,20 +137,16 @@ export function collectPackageIdsInDisplayOrder({ stack, sticky_footer, }) {
128
137
  * (`actualProps.visible !== false`).
129
138
  */
130
139
  export const resolveOverrideProperties = (context, overrides) => {
131
- if (!overrides)
132
- return {};
133
140
  let merged = {};
134
- for (const override of overrides) {
135
- if (override.conditions.every((c) => conditionMatches(c, context))) {
136
- const properties = { ...override.properties };
137
- const canReveal = override.conditions.some((c) => c.type === RuleKey.StateCondition);
138
- // visible: null carries no intent and must not clobber an earlier false.
139
- if (properties.visible === null ||
140
- (properties.visible === true && !canReveal)) {
141
- delete properties.visible;
142
- }
143
- merged = { ...merged, ...properties };
141
+ for (const override of filterMatchingOverrides(context, overrides)) {
142
+ const properties = { ...override.properties };
143
+ const canReveal = override.conditions.some((c) => c.type === RuleKey.StateCondition);
144
+ // visible: null carries no intent and must not clobber an earlier false.
145
+ if (properties.visible === null ||
146
+ (properties.visible === true && !canReveal)) {
147
+ delete properties.visible;
144
148
  }
149
+ merged = { ...merged, ...properties };
145
150
  }
146
151
  return merged;
147
152
  };
@@ -201,8 +206,8 @@ const conditionMatches = (condition, context) => {
201
206
  if (condition.type === RuleKey.StateCondition) {
202
207
  const stateValues = context.stateValues ?? {};
203
208
  const stateDefaults = context.stateDefaults ?? {};
204
- const hasCurrentValue = Object.hasOwn(stateValues, condition.name);
205
- const hasDeclaredDefault = Object.hasOwn(stateDefaults, condition.name);
209
+ const hasCurrentValue = Object.prototype.hasOwnProperty.call(stateValues, condition.name);
210
+ const hasDeclaredDefault = Object.prototype.hasOwnProperty.call(stateDefaults, condition.name);
206
211
  // Undeclared key (not in store, not declared): false for BOTH operators.
207
212
  // This diverges from variable_condition (where != matches a missing variable):
208
213
  // state keys are editor-managed, so an undeclared reference is always a bug,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@revenuecat/purchases-ui-js",
3
3
  "description": "Web components for Paywalls. Powered by RevenueCat",
4
4
  "private": false,
5
- "version": "4.8.20",
5
+ "version": "4.8.22",
6
6
  "author": {
7
7
  "name": "RevenueCat, Inc."
8
8
  },