@payment-kit-js/vanilla 0.5.7 → 0.5.9

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.
@@ -63,6 +63,16 @@ function createVgsCardFields(form, mountPoints, css, onFocusChangeCallbacks, pla
63
63
  "box-sizing": "border-box",
64
64
  ...containerPadding && containerPadding !== "0px" ? { padding: containerPadding } : {}
65
65
  };
66
+ for (const selector of [
67
+ mountPoints.pan,
68
+ mountPoints.exp,
69
+ mountPoints.cvc
70
+ ]) {
71
+ const container = document.querySelector(selector);
72
+ if (!container) continue;
73
+ const staleIframes = container.querySelectorAll("iframe");
74
+ for (const iframe of staleIframes) iframe.remove();
75
+ }
66
76
  const panField = form.cardNumberField(mountPoints.pan, {
67
77
  placeholder: placeholders?.card_pan ?? "Card number",
68
78
  validations: ["required", "validCardNumber"],
@@ -88,6 +98,8 @@ function createVgsCardFields(form, mountPoints, css, onFocusChangeCallbacks, pla
88
98
  validations: ["required", "validCardSecurityCode"],
89
99
  css: fieldCss
90
100
  });
101
+ const observers = [];
102
+ const containers = [];
91
103
  for (const selector of [
92
104
  mountPoints.pan,
93
105
  mountPoints.exp,
@@ -95,6 +107,7 @@ function createVgsCardFields(form, mountPoints, css, onFocusChangeCallbacks, pla
95
107
  ]) {
96
108
  const container = document.querySelector(selector);
97
109
  if (!container) continue;
110
+ containers.push(container);
98
111
  const styleIframe = (iframe) => {
99
112
  const containerEl = container;
100
113
  if (getComputedStyle(containerEl).position === "static") containerEl.style.position = "relative";
@@ -104,18 +117,17 @@ function createVgsCardFields(form, mountPoints, css, onFocusChangeCallbacks, pla
104
117
  iframe.style.height = "100%";
105
118
  iframe.style.border = "none";
106
119
  };
107
- const existing = container.querySelector("iframe");
108
- if (existing) styleIframe(existing);
109
- else new MutationObserver((_, obs) => {
120
+ const observer = new MutationObserver(() => {
110
121
  const iframe = container.querySelector("iframe");
111
- if (iframe) {
112
- styleIframe(iframe);
113
- obs.disconnect();
114
- }
115
- }).observe(container, {
122
+ if (iframe && iframe.style.position !== "absolute") styleIframe(iframe);
123
+ });
124
+ observer.observe(container, {
116
125
  childList: true,
117
126
  subtree: true
118
127
  });
128
+ observers.push(observer);
129
+ const existing = container.querySelector("iframe");
130
+ if (existing && existing.style.position !== "absolute") styleIframe(existing);
119
131
  }
120
132
  if (onFocusChangeCallbacks) {
121
133
  const fieldMap = [
@@ -131,8 +143,15 @@ function createVgsCardFields(form, mountPoints, css, onFocusChangeCallbacks, pla
131
143
  }
132
144
  }
133
145
  }
146
+ return () => {
147
+ for (const observer of observers) observer.disconnect();
148
+ for (const container of containers) {
149
+ const iframes = container.querySelectorAll("iframe");
150
+ for (const iframe of iframes) iframe.remove();
151
+ }
152
+ };
134
153
  }
135
154
 
136
155
  //#endregion
137
156
  export { initVgsCollect as n, loadVgsCollectScript as r, createVgsCardFields as t };
138
- //# sourceMappingURL=vgs-collect-loader-3Z4IWCA6.mjs.map
157
+ //# sourceMappingURL=vgs-collect-loader-BZj36d8r.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vgs-collect-loader-BZj36d8r.mjs","names":["loadPromise: Promise<void> | null","observers: MutationObserver[]","containers: Element[]","fieldMap: [VGSCollectField, string][]"],"sources":["../src/payment-methods/vgs-collect-loader.ts"],"sourcesContent":["const VGS_COLLECT_CDN = \"https://js.verygoodvault.com/vgs-collect/3.2.1/vgs-collect.js\";\n\nlet loadPromise: Promise<void> | null = null;\n\n/**\n * Loads VGS Collect JS from CDN. Idempotent — subsequent calls return the same promise.\n */\nexport function loadVgsCollectScript(): Promise<void> {\n if (loadPromise) {\n console.log(\"[PaymentKit] VGS Collect CDN load already in progress — reusing promise\");\n return loadPromise;\n }\n\n loadPromise = new Promise<void>((resolve, reject) => {\n if (typeof window !== \"undefined\" && window.VGSCollect) {\n console.log(\"[PaymentKit] VGS Collect JS already available on window\");\n resolve();\n return;\n }\n\n console.log(`[PaymentKit] Loading VGS Collect JS from CDN: ${VGS_COLLECT_CDN}`);\n const script = document.createElement(\"script\");\n script.src = VGS_COLLECT_CDN;\n script.async = true;\n script.onload = () => {\n console.log(\"[PaymentKit] VGS Collect JS loaded successfully from CDN\");\n resolve();\n };\n script.onerror = () => {\n console.error(`[PaymentKit] Failed to load VGS Collect JS from CDN: ${VGS_COLLECT_CDN}`);\n loadPromise = null;\n reject(new Error(\"Failed to load VGS Collect JS\"));\n };\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\n/**\n * Initializes a VGS Collect form instance.\n */\nexport function initVgsCollect(vaultId: string, environment: string): VGSCollectForm {\n if (!window.VGSCollect) {\n throw new Error(\"VGS Collect JS not loaded. Call loadVgsCollectScript() first.\");\n }\n return window.VGSCollect.create(vaultId, environment, () => {});\n}\n\n/**\n * Creates and mounts VGS Collect card fields using CMP-specific field methods.\n *\n * Uses cardNumberField(), cardExpirationDateField(), cardCVCField() which are\n * required for the CMP createCard() flow. After mounting, call form.createCard()\n * to create a card in the VGS vault, which returns aliases for PAN and CVC.\n *\n * @param form - VGS Collect form instance\n * @param mountPoints - CSS selectors for PAN, expiry, and CVC containers\n * @param css - Optional CSS to apply to VGS Collect iframe fields\n */\nexport function createVgsCardFields(\n form: VGSCollectForm,\n mountPoints: { pan: string; exp: string; cvc: string },\n css?: Record<string, string>,\n onFocusChangeCallbacks?: Partial<Record<string, (isFocused: boolean) => void>>,\n placeholders?: Partial<Record<string, string>>,\n): () => void {\n // Read container padding so the VGS input content matches the merchant's\n // visual intent. The iframe uses absolute positioning (inset: 0) to fill\n // the full container, so the input needs its own padding for alignment.\n const sampleContainer = document.querySelector(mountPoints.pan) as HTMLElement | null;\n const containerPadding = sampleContainer ? getComputedStyle(sampleContainer).padding : undefined;\n\n const baseCss = css ?? {\n \"font-size\": \"1rem\",\n color: \"#18181b\",\n \"font-family\": \"ui-sans-serif, system-ui, sans-serif\",\n };\n\n const fieldCss = {\n ...baseCss,\n height: \"100%\",\n \"box-sizing\": \"border-box\",\n ...(containerPadding && containerPadding !== \"0px\" ? { padding: containerPadding } : {}),\n };\n\n // Remove stale iframes from previous VGS form mounts to prevent overlap.\n // VGS's form.unmount() does not remove iframes from the DOM.\n // IMPORTANT: This must run BEFORE VGS field methods below, because VGS may\n // insert new iframes synchronously — removing after would destroy fresh iframes.\n for (const selector of [mountPoints.pan, mountPoints.exp, mountPoints.cvc]) {\n const container = document.querySelector(selector);\n if (!container) continue;\n const staleIframes = container.querySelectorAll(\"iframe\");\n for (const iframe of staleIframes) {\n iframe.remove();\n }\n }\n\n const panField = form.cardNumberField(mountPoints.pan, {\n placeholder: placeholders?.card_pan ?? \"Card number\",\n validations: [\"required\", \"validCardNumber\"],\n css: fieldCss,\n });\n\n const expField = form.field(mountPoints.exp, {\n type: \"card-expiration-date\",\n name: \"exp-date\",\n placeholder: placeholders?.card_exp ?? \"MM / YY\",\n yearLength: 2,\n serializers: [{ name: \"separate\", options: { monthName: \"month\", yearName: \"year\" } }],\n validations: [\"required\", \"validCardExpirationDate\"],\n css: fieldCss,\n });\n\n const cvcField = form.cardCVCField(mountPoints.cvc, {\n placeholder: placeholders?.card_cvc ?? \"123\",\n validations: [\"required\", \"validCardSecurityCode\"],\n css: fieldCss,\n });\n\n // Track observers for cleanup\n const observers: MutationObserver[] = [];\n const containers: Element[] = [];\n\n // VGS Collect doesn't set width/height on its iframes, so they may not\n // fill the merchant's container. Force them to stretch like direct-mode iframes.\n // Iframes may be inserted asynchronously, so use MutationObserver as a fallback.\n for (const selector of [mountPoints.pan, mountPoints.exp, mountPoints.cvc]) {\n const container = document.querySelector(selector);\n if (!container) continue;\n containers.push(container);\n\n const styleIframe = (iframe: HTMLIFrameElement) => {\n // Use absolute positioning so the iframe covers the full container\n // including any padding the merchant's CSS applies.\n const containerEl = container as HTMLElement;\n if (getComputedStyle(containerEl).position === \"static\") {\n containerEl.style.position = \"relative\";\n }\n iframe.style.position = \"absolute\";\n iframe.style.inset = \"0\";\n iframe.style.width = \"100%\";\n iframe.style.height = \"100%\";\n iframe.style.border = \"none\";\n };\n\n // Always set up a MutationObserver to catch asynchronously inserted iframes.\n // VGS inserts iframes after field creation, so we need the observer even if\n // no iframe exists yet.\n const observer = new MutationObserver(() => {\n const iframe = container.querySelector(\"iframe\") as HTMLIFrameElement | null;\n if (iframe && iframe.style.position !== \"absolute\") {\n styleIframe(iframe);\n }\n });\n observer.observe(container, { childList: true, subtree: true });\n observers.push(observer);\n\n // Style any iframe VGS may have inserted synchronously during field creation.\n // The MutationObserver only fires on future mutations, so this handles the\n // case where VGS inserts the iframe before the observer is attached.\n const existing = container.querySelector(\"iframe\") as HTMLIFrameElement | null;\n if (existing && existing.style.position !== \"absolute\") {\n styleIframe(existing);\n }\n }\n\n if (onFocusChangeCallbacks) {\n const fieldMap: [VGSCollectField, string][] = [\n [panField, \"card_pan\"],\n [expField, \"card_exp\"],\n [cvcField, \"card_cvc\"],\n ];\n for (const [field, key] of fieldMap) {\n const cb = onFocusChangeCallbacks[key];\n if (cb) {\n field.on(\"focus\", () => cb(true));\n field.on(\"blur\", () => cb(false));\n }\n }\n }\n\n // Return cleanup function to disconnect observers and remove iframes\n return () => {\n for (const observer of observers) {\n observer.disconnect();\n }\n for (const container of containers) {\n const iframes = container.querySelectorAll(\"iframe\");\n for (const iframe of iframes) {\n iframe.remove();\n }\n }\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payment-kit-js/vanilla",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "main": "./dist/index.mjs",
5
5
  "types": "./dist/index.d.mts",
6
6
  "module": "./dist/index.mjs",
@@ -30,7 +30,8 @@
30
30
  "dev": "yarn tsdown --watch ./src",
31
31
  "build:npm": "yarn tsdown",
32
32
  "build:cdn": "node build-cdn.mjs",
33
- "build": "yarn build:npm && yarn build:cdn"
33
+ "build": "yarn build:npm && yarn build:cdn",
34
+ "test": "vitest run"
34
35
  },
35
36
  "packageManager": "yarn@4.10.3",
36
37
  "dependencies": {
@@ -45,8 +46,10 @@
45
46
  "@stripe/stripe-js": "^8.6.1",
46
47
  "@types/applepayjs": "^14.0.9",
47
48
  "esbuild": "^0.24.2",
49
+ "jsdom": "^26.1.0",
48
50
  "tsdown": "^0.15.10",
49
- "typescript": "^5.9.3"
51
+ "typescript": "^5.9.3",
52
+ "vitest": "^3.1.4"
50
53
  },
51
- "stableVersion": "0.5.7"
54
+ "stableVersion": "0.5.9"
52
55
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"types-CZk3V0Qt.d.mts","names":[],"sources":["../src/analytics/checkout-timing.ts","../src/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAmBA;;;;AC0CC;AAID;AAEA;;;;;AAQA;;AAGkB,cD3DL,qBAAA,CC2DK;EASS,eAAA,gBAAA;EAArB,QAAA,SAAA;EAC4B,QAAA,iBAAA;EAAxB,QAAA,SAAA;EAAuB,QAAA,iBAAA;EAI5B,WAAA,CAAA,iBAAuB,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA;EAAoB;;;;;EAI/B,cAAA,CAAA,CAAA,EAAA,IAAA;EAEE;;AAInB;;;EAgBwB,eAAA,CAAA,CAAA,EAAA,IAAA;EAAO;AAG/B;;EAGa,WAAA,CAAA,CAAA,EAAA,IAAA;EACA;;;;AAUb;EAEY,YAAA,CAAA,iBAAa,EAAA,MAAA,CAAA,EAAA,IAAA;EAAgE;;;;;AAMzF;;EACQ,SAAA,CAAA,iBAAA,EAAA,MAAA,GAAA,IAAA,EAAA,SAAA,EAAA,MAAA,GAAA,IAAA,EAAA,YAAA,EAAA,MAAA,GAAA,IAAA,CAAA,EAAA,IAAA;EAAwB;;;EAAa,QAAA,YAAA;;;;;AA/E7C,KAFK,cAAA,GAEqB,eAAA,GAAA,gBAAA,GAAA,kBAAA,GAAA,mBAAA;AAEd,KAFA,cAAA,GAEc,UAAA,GAAA,SAAA;AAEd,KAFA,cAAA,GAEA;EAEkF,aAAA,EAAA,CAAA,MAAA,EAFlF,gBAEkF,EAAA,OAAA,CAAA,EAAA,OAAA,EAAA,GAAvF,OAAuF,CAAA;IAAvF,IAAA,EAAA;MAAO,CAAA,GAAA,EAAA,MAAA,CAAA,EAAA,OAAA;IAIF,CAAA;IAAiC,MAAA,CAAA,EAAA,KAAA;EAG3B,CAAA,GAAA;IASS,IAAA,CAAA,EAAA,KAAA;IAArB,MAAA,EAhBwF,gBAgBxF;EAC4B,CAAA,CAAA;EAAxB,OAAA,CAAA,EAAA,GAAA,GAAA,IAAA;CAAuB;AAI5B,KAjBO,UAAA,GAiBP,CAAA,UAAuB,SAjBiB,aAiBjB,CAAA,OAAA,CAAA,EAAA,CAAA,CAAA,OAAA,EAAA;EAAoB,WAAA,EAAA,MAAA;EACT,WAAA,EAAA,MAAA;EAArB,cAAA,EAfA,CAeA;EAEA;EAAR,oBAAA,CAAA,EAAA,QAAA,GAAA,KAAA;EACO;EAEE,UAAA,CAAA,EAAA,MAAA;EAAgB;EAIvB,cAAA,CAAA,EAAA,MAAgB;EAKP;EACJ,yBAAA,CAAA,EAAA,OAAA;CAUO,EAAA,GA/BlB,oBA+BkB,CA/BG,CA+BH,CAAA,GAAA;EAAO,MAAA,EA9BrB,uBA8BqB,CA9BG,CA8BH,CAAA;EAGnB,OAAA,EAAA,GAAA,GAAA,IAAgB;CAEf;KA/BR,uBAgCQ,CAAA,UAAA,SAhCmC,aAgCnC,CAAA,OAAA,CAAA,EAAA,CAAA,GAAA,CAAA,UAAA,MA/BK,oBA+BL,CA/B0B,CA+B1B,CAAA,CAAA,CAAA,OAAA,EAAA;EACA,MAAA,EA9BH,OA8BG,CA9BK,gBA8BL,CAAA;EAQC,aAAA,EArCG,CAqCH;EAAkB,OAAA,CAAA,EAAA,OAAA;EAAc,OAAA,EAAA,CAAA,KAAA,EAnC3B,gBAmC2B,EAAA,GAAA,IAAA;EAElC,SAAA,EAAA,CAAA,IAAA,EAAA;IAEA,CAAA,GAAA,EAAA,MAAa,CAAA,EAAA,OAAA;EAAgE,CAAA,EAAA,GAAA,IAAA;CACjF,EAAA,GAAA,IAAA;AACS,KArCL,gBAAA,GAqCK;EACA,OAAA,EAAA,MAAA;EAAc,UAAA,EAAA,MAAA;EAGnB,WAAA,EAAA,MAAA;EAAwC,WAAA,EAAA,MAAA;EAC5C,iBAAA,EArCa,uBAqCb;EAAwB,aAAA,EApCf,qBAoCe;EAAX;EAAmC,iBAAA,EAAA,MAAA;EAAX;EAAU,oBAAA,CAAA,EAAA,QAAA,GAAA,KAAA;;;;;;wBA1B/B;;KAGZ,gBAAA;;aAEC;aACA;aACA;;;;;;;;cAQC,kBAAkB;KAEpB,gBAAA,aAA6B;KAE7B,6EAA6E;QACjF;iBACS;iBACA;;KAGL,wCAAwC,oCAC5C,aAAa,WAAW,aAAa,WAAW"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"vgs-collect-loader-3Z4IWCA6.mjs","names":["loadPromise: Promise<void> | null","fieldMap: [VGSCollectField, string][]"],"sources":["../src/payment-methods/vgs-collect-loader.ts"],"sourcesContent":["const VGS_COLLECT_CDN = \"https://js.verygoodvault.com/vgs-collect/3.2.1/vgs-collect.js\";\n\nlet loadPromise: Promise<void> | null = null;\n\n/**\n * Loads VGS Collect JS from CDN. Idempotent — subsequent calls return the same promise.\n */\nexport function loadVgsCollectScript(): Promise<void> {\n if (loadPromise) {\n console.log(\"[PaymentKit] VGS Collect CDN load already in progress — reusing promise\");\n return loadPromise;\n }\n\n loadPromise = new Promise<void>((resolve, reject) => {\n if (typeof window !== \"undefined\" && window.VGSCollect) {\n console.log(\"[PaymentKit] VGS Collect JS already available on window\");\n resolve();\n return;\n }\n\n console.log(`[PaymentKit] Loading VGS Collect JS from CDN: ${VGS_COLLECT_CDN}`);\n const script = document.createElement(\"script\");\n script.src = VGS_COLLECT_CDN;\n script.async = true;\n script.onload = () => {\n console.log(\"[PaymentKit] VGS Collect JS loaded successfully from CDN\");\n resolve();\n };\n script.onerror = () => {\n console.error(`[PaymentKit] Failed to load VGS Collect JS from CDN: ${VGS_COLLECT_CDN}`);\n loadPromise = null;\n reject(new Error(\"Failed to load VGS Collect JS\"));\n };\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\n/**\n * Initializes a VGS Collect form instance.\n */\nexport function initVgsCollect(vaultId: string, environment: string): VGSCollectForm {\n if (!window.VGSCollect) {\n throw new Error(\"VGS Collect JS not loaded. Call loadVgsCollectScript() first.\");\n }\n return window.VGSCollect.create(vaultId, environment, () => {});\n}\n\n/**\n * Creates and mounts VGS Collect card fields using CMP-specific field methods.\n *\n * Uses cardNumberField(), cardExpirationDateField(), cardCVCField() which are\n * required for the CMP createCard() flow. After mounting, call form.createCard()\n * to create a card in the VGS vault, which returns aliases for PAN and CVC.\n *\n * @param form - VGS Collect form instance\n * @param mountPoints - CSS selectors for PAN, expiry, and CVC containers\n * @param css - Optional CSS to apply to VGS Collect iframe fields\n */\nexport function createVgsCardFields(\n form: VGSCollectForm,\n mountPoints: { pan: string; exp: string; cvc: string },\n css?: Record<string, string>,\n onFocusChangeCallbacks?: Partial<Record<string, (isFocused: boolean) => void>>,\n placeholders?: Partial<Record<string, string>>,\n) {\n // Read container padding so the VGS input content matches the merchant's\n // visual intent. The iframe uses absolute positioning (inset: 0) to fill\n // the full container, so the input needs its own padding for alignment.\n const sampleContainer = document.querySelector(mountPoints.pan) as HTMLElement | null;\n const containerPadding = sampleContainer ? getComputedStyle(sampleContainer).padding : undefined;\n\n const baseCss = css ?? {\n \"font-size\": \"1rem\",\n color: \"#18181b\",\n \"font-family\": \"ui-sans-serif, system-ui, sans-serif\",\n };\n\n const fieldCss = {\n ...baseCss,\n height: \"100%\",\n \"box-sizing\": \"border-box\",\n ...(containerPadding && containerPadding !== \"0px\" ? { padding: containerPadding } : {}),\n };\n\n const panField = form.cardNumberField(mountPoints.pan, {\n placeholder: placeholders?.card_pan ?? \"Card number\",\n validations: [\"required\", \"validCardNumber\"],\n css: fieldCss,\n });\n\n const expField = form.field(mountPoints.exp, {\n type: \"card-expiration-date\",\n name: \"exp-date\",\n placeholder: placeholders?.card_exp ?? \"MM / YY\",\n yearLength: 2,\n serializers: [{ name: \"separate\", options: { monthName: \"month\", yearName: \"year\" } }],\n validations: [\"required\", \"validCardExpirationDate\"],\n css: fieldCss,\n });\n\n const cvcField = form.cardCVCField(mountPoints.cvc, {\n placeholder: placeholders?.card_cvc ?? \"123\",\n validations: [\"required\", \"validCardSecurityCode\"],\n css: fieldCss,\n });\n\n // VGS Collect doesn't set width/height on its iframes, so they may not\n // fill the merchant's container. Force them to stretch like direct-mode iframes.\n // Iframes may be inserted asynchronously, so use MutationObserver as a fallback.\n for (const selector of [mountPoints.pan, mountPoints.exp, mountPoints.cvc]) {\n const container = document.querySelector(selector);\n if (!container) continue;\n\n const styleIframe = (iframe: HTMLIFrameElement) => {\n // Use absolute positioning so the iframe covers the full container\n // including any padding the merchant's CSS applies.\n const containerEl = container as HTMLElement;\n if (getComputedStyle(containerEl).position === \"static\") {\n containerEl.style.position = \"relative\";\n }\n iframe.style.position = \"absolute\";\n iframe.style.inset = \"0\";\n iframe.style.width = \"100%\";\n iframe.style.height = \"100%\";\n iframe.style.border = \"none\";\n };\n\n const existing = container.querySelector(\"iframe\") as HTMLIFrameElement | null;\n if (existing) {\n styleIframe(existing);\n } else {\n const observer = new MutationObserver((_, obs) => {\n const iframe = container.querySelector(\"iframe\") as HTMLIFrameElement | null;\n if (iframe) {\n styleIframe(iframe);\n obs.disconnect();\n }\n });\n observer.observe(container, { childList: true, subtree: true });\n }\n }\n\n if (onFocusChangeCallbacks) {\n const fieldMap: [VGSCollectField, string][] = [\n [panField, \"card_pan\"],\n [expField, \"card_exp\"],\n [cvcField, \"card_cvc\"],\n ];\n for (const [field, key] of fieldMap) {\n const cb = onFocusChangeCallbacks[key];\n if (cb) {\n field.on(\"focus\", () => cb(true));\n field.on(\"blur\", () => cb(false));\n }\n }\n }\n}\n"],"mappings":"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"}