@whop/elements 1.3.0 → 1.5.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.
package/checkout.d.cts CHANGED
@@ -152,10 +152,10 @@ export interface CheckoutElementCreateOptions {
152
152
  */
153
153
  export interface ExpressCheckoutElementProps {
154
154
  /**
155
- * Which wallets may render — a filter over what the buyer’s device actually offers. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first whatever order this lists. An empty list renders nothing (warned in dev builds). Both wallets need the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
156
- * @default ["apple_pay","google_pay"]
155
+ * Which wallets may render — a filter over what the buyer’s device and the seller actually offer. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first, with PayPal last, whatever order this lists. An empty list renders nothing (warned in dev builds). Every wallet needs the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
156
+ * @default ["apple_pay","google_pay","paypal_express"]
157
157
  */
158
- wallets?: ("apple_pay" | "google_pay")[];
158
+ wallets?: ("apple_pay" | "google_pay" | "paypal_express")[];
159
159
  /**
160
160
  * How the buttons stack. `auto` follows the element’s own container — stacked in narrow containers, side by side in wide ones; `horizontal` and `vertical` force one arrangement.
161
161
  * @default "auto"
@@ -193,10 +193,10 @@ export interface ExpressCheckoutElementHandle {
193
193
  */
194
194
  export interface ExpressCheckoutElementCreateOptions {
195
195
  /**
196
- * Which wallets may render — a filter over what the buyer’s device actually offers. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first whatever order this lists. An empty list renders nothing (warned in dev builds). Both wallets need the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
197
- * @default ["apple_pay","google_pay"]
196
+ * Which wallets may render — a filter over what the buyer’s device and the seller actually offer. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first, with PayPal last, whatever order this lists. An empty list renders nothing (warned in dev builds). Every wallet needs the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
197
+ * @default ["apple_pay","google_pay","paypal_express"]
198
198
  */
199
- wallets?: ("apple_pay" | "google_pay")[];
199
+ wallets?: ("apple_pay" | "google_pay" | "paypal_express")[];
200
200
  /**
201
201
  * How the buttons stack. `auto` follows the element’s own container — stacked in narrow containers, side by side in wide ones; `horizontal` and `vertical` force one arrangement.
202
202
  * @default "auto"
@@ -227,7 +227,7 @@ export interface CheckoutHandle {
227
227
  */
228
228
  create(name: "checkout", options?: CheckoutElementCreateOptions): CheckoutElementHandle;
229
229
  /**
230
- * One-press Apple Pay and Google Pay buttons for a checkout — the OS payment sheet collects whatever the session still needs (the buyer's email, a phone number where the seller collects one, a shipping address for physical goods, and in Apple Pay a promo code where the seller offers entry) and one press finishes the purchase. It shares the checkout's entry slot with the full checkout element, so a handle mounts exactly one of the two: this button alone where the purchase is simple enough for a sheet to finish, or the full checkout surface for everything else. Renders only the wallets the buyer's device can actually pay with — and the checkout's own payment method configuration allows — using the served button art, and renders nothing where no wallet is available. Both wallets require the page's domain to be a verified payment method domain: first-party whop.com pages are pre-approved, and any other site must register its domain through the Payment Method Domains API before either button renders there. Offering Google Pay on your own site is subject to the Google Pay API Terms of Service. A checkout the sheet cannot honestly finish — a waitlist join, a transfer, a form question only a page can ask — refuses loudly instead of rendering a doomed button.
230
+ * One-press Apple Pay, Google Pay and PayPal buttons for a checkout — the wallet collects whatever the session still needs (the buyer's email, a shipping address for physical goods, a phone number where the seller collects one in Apple Pay and Google Pay, and in Apple Pay a promo code where the seller offers entry) and one press finishes the purchase. It shares the checkout's entry slot with the full checkout element, so a handle mounts exactly one of the two: this button alone where the purchase is simple enough for a sheet to finish, or the full checkout surface for everything else. Renders only the wallets the buyer's device can actually pay with — and the checkout's own payment method configuration allows — using the served button art, and renders nothing where no wallet is available. PayPal appears on one-time purchases where the seller accepts it and opens in its own window. All three wallets require the page's domain to be a verified payment method domain: first-party whop.com pages are pre-approved, and any other site must register its domain through the Payment Method Domains API before any of these buttons renders there. Offering Google Pay on your own site is subject to the Google Pay API Terms of Service. A checkout the sheet cannot honestly finish — a waitlist join, a transfer, a form question only a page can ask — refuses loudly instead of rendering a doomed button.
231
231
  * Exclusive slot `entry`. Mount exactly one of: checkout | expressCheckout.
232
232
  */
233
233
  create(name: "expressCheckout", options?: ExpressCheckoutElementCreateOptions): ExpressCheckoutElementHandle;
package/checkout.d.ts CHANGED
@@ -152,10 +152,10 @@ export interface CheckoutElementCreateOptions {
152
152
  */
153
153
  export interface ExpressCheckoutElementProps {
154
154
  /**
155
- * Which wallets may render — a filter over what the buyer’s device actually offers. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first whatever order this lists. An empty list renders nothing (warned in dev builds). Both wallets need the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
156
- * @default ["apple_pay","google_pay"]
155
+ * Which wallets may render — a filter over what the buyer’s device and the seller actually offer. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first, with PayPal last, whatever order this lists. An empty list renders nothing (warned in dev builds). Every wallet needs the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
156
+ * @default ["apple_pay","google_pay","paypal_express"]
157
157
  */
158
- wallets?: ("apple_pay" | "google_pay")[];
158
+ wallets?: ("apple_pay" | "google_pay" | "paypal_express")[];
159
159
  /**
160
160
  * How the buttons stack. `auto` follows the element’s own container — stacked in narrow containers, side by side in wide ones; `horizontal` and `vertical` force one arrangement.
161
161
  * @default "auto"
@@ -193,10 +193,10 @@ export interface ExpressCheckoutElementHandle {
193
193
  */
194
194
  export interface ExpressCheckoutElementCreateOptions {
195
195
  /**
196
- * Which wallets may render — a filter over what the buyer’s device actually offers. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first whatever order this lists. An empty list renders nothing (warned in dev builds). Both wallets need the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
197
- * @default ["apple_pay","google_pay"]
196
+ * Which wallets may render — a filter over what the buyer’s device and the seller actually offer. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first, with PayPal last, whatever order this lists. An empty list renders nothing (warned in dev builds). Every wallet needs the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
197
+ * @default ["apple_pay","google_pay","paypal_express"]
198
198
  */
199
- wallets?: ("apple_pay" | "google_pay")[];
199
+ wallets?: ("apple_pay" | "google_pay" | "paypal_express")[];
200
200
  /**
201
201
  * How the buttons stack. `auto` follows the element’s own container — stacked in narrow containers, side by side in wide ones; `horizontal` and `vertical` force one arrangement.
202
202
  * @default "auto"
@@ -227,7 +227,7 @@ export interface CheckoutHandle {
227
227
  */
228
228
  create(name: "checkout", options?: CheckoutElementCreateOptions): CheckoutElementHandle;
229
229
  /**
230
- * One-press Apple Pay and Google Pay buttons for a checkout — the OS payment sheet collects whatever the session still needs (the buyer's email, a phone number where the seller collects one, a shipping address for physical goods, and in Apple Pay a promo code where the seller offers entry) and one press finishes the purchase. It shares the checkout's entry slot with the full checkout element, so a handle mounts exactly one of the two: this button alone where the purchase is simple enough for a sheet to finish, or the full checkout surface for everything else. Renders only the wallets the buyer's device can actually pay with — and the checkout's own payment method configuration allows — using the served button art, and renders nothing where no wallet is available. Both wallets require the page's domain to be a verified payment method domain: first-party whop.com pages are pre-approved, and any other site must register its domain through the Payment Method Domains API before either button renders there. Offering Google Pay on your own site is subject to the Google Pay API Terms of Service. A checkout the sheet cannot honestly finish — a waitlist join, a transfer, a form question only a page can ask — refuses loudly instead of rendering a doomed button.
230
+ * One-press Apple Pay, Google Pay and PayPal buttons for a checkout — the wallet collects whatever the session still needs (the buyer's email, a shipping address for physical goods, a phone number where the seller collects one in Apple Pay and Google Pay, and in Apple Pay a promo code where the seller offers entry) and one press finishes the purchase. It shares the checkout's entry slot with the full checkout element, so a handle mounts exactly one of the two: this button alone where the purchase is simple enough for a sheet to finish, or the full checkout surface for everything else. Renders only the wallets the buyer's device can actually pay with — and the checkout's own payment method configuration allows — using the served button art, and renders nothing where no wallet is available. PayPal appears on one-time purchases where the seller accepts it and opens in its own window. All three wallets require the page's domain to be a verified payment method domain: first-party whop.com pages are pre-approved, and any other site must register its domain through the Payment Method Domains API before any of these buttons renders there. Offering Google Pay on your own site is subject to the Google Pay API Terms of Service. A checkout the sheet cannot honestly finish — a waitlist join, a transfer, a form question only a page can ask — refuses loudly instead of rendering a doomed button.
231
231
  * Exclusive slot `entry`. Mount exactly one of: checkout | expressCheckout.
232
232
  */
233
233
  create(name: "expressCheckout", options?: ExpressCheckoutElementCreateOptions): ExpressCheckoutElementHandle;
package/index.cjs CHANGED
@@ -22,18 +22,27 @@ var npm_loader_exports = {};
22
22
  __export(npm_loader_exports, {
23
23
  SDK_URL: () => SDK_URL,
24
24
  WHOP_ELEMENTS_LOCALES: () => WHOP_ELEMENTS_LOCALES,
25
+ contentSecurityPolicy: () => contentSecurityPolicy,
25
26
  loadWhop: () => loadWhop,
26
27
  preloadHints: () => preloadHints
27
28
  });
28
29
  module.exports = __toCommonJS(npm_loader_exports);
29
30
 
31
+ // <define:__WHOP_ELEMENTS_API_ORIGINS__>
32
+ var define_WHOP_ELEMENTS_API_ORIGINS_default = { production: "https://api.whop.com", sandbox: "https://sandbox-api.whop.com" };
33
+
30
34
  // <define:__WHOP_ELEMENTS_LOCALES__>
31
35
  var define_WHOP_ELEMENTS_LOCALES_default = ["en", "es", "zh", "nl", "pt", "de", "hu", "it", "fr", "ja", "pl", "tr"];
32
36
 
37
+ // <define:__WHOP_ELEMENTS_PAGE_REQUIREMENTS__>
38
+ var define_WHOP_ELEMENTS_PAGE_REQUIREMENTS_default = { wallets: { production: { sources: { "frame-src": ["https://checkout.paypal.com", "https://assets.braintreegateway.com", "https://www.paypal.com"], "script-src": ["https://pay.google.com", "https://applepay.cdn-apple.com", "https://js.braintreegateway.com", "https://www.paypal.com", "https://c.paypal.com"], "connect-src": ["https://pay.google.com", "https://google.com", "https://account.google.com", "https://www.google.com", "https://api.basistheory.com", "https://payments.braintree-api.com", "https://api.braintreegateway.com", "https://api-m.paypal.com", "https://www.paypal.com"], "img-src": ["https://www.paypalobjects.com"] }, hints: [{ rel: "preload", href: "https://pay.google.com/gp/p/js/pay.js", as: "script" }, { rel: "preload", href: "https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js", as: "script", crossOrigin: "anonymous" }, { rel: "preload", href: "https://js.braintreegateway.com/web/3.146.0/js/paypal-checkout-v6.min.js", as: "script" }, { rel: "preload", href: "https://www.paypal.com/web-sdk/v6/core", as: "script" }, { rel: "preconnect", href: "https://api.basistheory.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://payments.braintree-api.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://api.braintreegateway.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://checkout.paypal.com" }, { rel: "preconnect", href: "https://assets.braintreegateway.com" }, { rel: "preconnect", href: "https://c.paypal.com" }, { rel: "preconnect", href: "https://api-m.paypal.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://www.paypal.com", crossOrigin: "anonymous" }] }, sandbox: { sources: { "frame-src": ["https://checkout.paypal.com", "https://assets.braintreegateway.com", "https://www.sandbox.paypal.com"], "script-src": ["https://pay.google.com", "https://applepay.cdn-apple.com", "https://js.braintreegateway.com", "https://www.sandbox.paypal.com", "https://c.paypal.com"], "connect-src": ["https://pay.google.com", "https://google.com", "https://account.google.com", "https://www.google.com", "https://api.basistheory.com", "https://payments.sandbox.braintree-api.com", "https://api.sandbox.braintreegateway.com", "https://api-m.sandbox.paypal.com", "https://www.sandbox.paypal.com"], "img-src": ["https://www.paypalobjects.com"] }, hints: [{ rel: "preload", href: "https://pay.google.com/gp/p/js/pay.js", as: "script" }, { rel: "preload", href: "https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js", as: "script", crossOrigin: "anonymous" }, { rel: "preload", href: "https://js.braintreegateway.com/web/3.146.0/js/paypal-checkout-v6.min.js", as: "script" }, { rel: "preload", href: "https://www.sandbox.paypal.com/web-sdk/v6/core", as: "script" }, { rel: "preconnect", href: "https://api.basistheory.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://payments.sandbox.braintree-api.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://api.sandbox.braintreegateway.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://checkout.paypal.com" }, { rel: "preconnect", href: "https://assets.braintreegateway.com" }, { rel: "preconnect", href: "https://c.paypal.com" }, { rel: "preconnect", href: "https://api-m.sandbox.paypal.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://www.sandbox.paypal.com", crossOrigin: "anonymous" }] } } };
39
+
33
40
  // ../../packages/elements-codegen/src/cli/build/npm-loader.ts
34
41
  var ORIGIN = "https://cdn.whop.com";
35
42
  var SDK_URL = "https://cdn.whop.com/elements/amber/elements.js";
36
43
  var WHOP_ELEMENTS_LOCALES = define_WHOP_ELEMENTS_LOCALES_default;
44
+ var API_ORIGINS = define_WHOP_ELEMENTS_API_ORIGINS_default;
45
+ var PAGE_REQUIREMENTS = define_WHOP_ELEMENTS_PAGE_REQUIREMENTS_default;
37
46
  var TAG = "script[data-whop-elements]";
38
47
  var resolved = null;
39
48
  function withRetry(promise) {
@@ -131,9 +140,187 @@ function watch(w, script, created) {
131
140
  }
132
141
  return script.__whopReady;
133
142
  }
134
- function preloadHints() {
135
- return [
136
- { rel: "preconnect", href: ORIGIN },
137
- { rel: "preload", href: SDK_URL, as: "script" }
138
- ];
143
+ function environmentOf(environment) {
144
+ if (environment === void 0 || environment === "production") return "production";
145
+ if (environment === "sandbox") return "sandbox";
146
+ throw new Error(`unknown environment ${JSON.stringify(environment)}: expected "production" or "sandbox"`);
147
+ }
148
+ function apiOrigin(environment) {
149
+ return API_ORIGINS[environmentOf(environment)];
150
+ }
151
+ function escapeAttribute(value) {
152
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
153
+ }
154
+ var PreloadHints = class extends Array {
155
+ // map/flatMap produce plain arrays: rendered <link> tags are not a hint list. The methods below
156
+ // keep hints as hints, so a narrowed list still has toLinkHeader() and friends.
157
+ static get [Symbol.species]() {
158
+ return Array;
159
+ }
160
+ filter(predicate, thisArg) {
161
+ return hintsFrom(super.filter(predicate, thisArg));
162
+ }
163
+ slice(start, end) {
164
+ return hintsFrom(super.slice(start, end));
165
+ }
166
+ concat(...items) {
167
+ return hintsFrom(super.concat(...items));
168
+ }
169
+ // copies sorted or reversed without Array.prototype.toSorted/toReversed, which older browsers lack.
170
+ toSorted(compareFn) {
171
+ return hintsFrom(this).sort(compareFn);
172
+ }
173
+ toReversed() {
174
+ const hints = hintsFrom(this);
175
+ hints.reverse();
176
+ return hints;
177
+ }
178
+ toLinkHeader() {
179
+ return this.map((h) => `<${h.href}>; rel=${h.rel}${h.as ? `; as=${h.as}` : ""}${h.crossOrigin ? `; crossorigin=${h.crossOrigin}` : ""}`).join(", ");
180
+ }
181
+ toHtml() {
182
+ return this.map((h) => `<link rel="${escapeAttribute(h.rel)}" href="${escapeAttribute(h.href)}"${h.as ? ` as="${escapeAttribute(h.as)}"` : ""}${h.crossOrigin ? ` crossorigin="${h.crossOrigin}"` : ""}>`).join("\n");
183
+ }
184
+ applyTo(headers) {
185
+ const value = this.toLinkHeader();
186
+ if (value) headers.append("Link", value);
187
+ }
188
+ };
189
+ function hintsFrom(items) {
190
+ const hints = new PreloadHints();
191
+ for (const item of items) hints.push(item);
192
+ return hints;
193
+ }
194
+ function preloadHints(options = {}) {
195
+ const hints = new PreloadHints();
196
+ const add = (hint) => {
197
+ if (!hints.some((h) => h.rel === hint.rel && h.href === hint.href && h.crossOrigin === hint.crossOrigin)) hints.push(hint);
198
+ };
199
+ add({ rel: "preconnect", href: ORIGIN });
200
+ add({ rel: "preload", href: SDK_URL, as: "script" });
201
+ add({ rel: "preconnect", href: apiOrigin(options.environment), crossOrigin: "anonymous" });
202
+ const environment = environmentOf(options.environment);
203
+ for (const [group, byEnvironment] of Object.entries(PAGE_REQUIREMENTS)) {
204
+ if (options[group] === false) continue;
205
+ for (const hint of byEnvironment[environment].hints) add(hint);
206
+ }
207
+ return hints;
208
+ }
209
+ var FALLBACKS = { "frame-src": ["child-src", "default-src"], "script-src": ["default-src"], "connect-src": ["default-src"], "img-src": ["default-src"] };
210
+ var POLICY_HEADERS = ["Content-Security-Policy", "Content-Security-Policy-Report-Only"];
211
+ var camelToKebab = (key) => key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
212
+ var kebabToCamel = (key) => key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
213
+ function addSources(values, sources) {
214
+ const out = values.filter((v) => typeof v !== "string" || v.toLowerCase() !== "'none'");
215
+ for (const source of sources) if (!out.includes(source)) out.push(source);
216
+ return out;
217
+ }
218
+ function mergeDirectives(directives, sources) {
219
+ for (const [name, list] of Object.entries(sources)) {
220
+ if (list.length === 0) continue;
221
+ const own = directives.find((d) => d.name === name);
222
+ const elem = name === "script-src" ? directives.find((d) => d.name === "script-src-elem") : void 0;
223
+ if (elem) elem.values = addSources(elem.values, list);
224
+ if (own) {
225
+ own.values = addSources(own.values, list);
226
+ continue;
227
+ }
228
+ const fallback = FALLBACKS[name].map((f) => directives.find((d) => d.name === f)).find((d) => d !== void 0);
229
+ if (fallback) directives.push({ name, values: addSources([...fallback.values], list) });
230
+ }
231
+ }
232
+ function parsePolicy(policy) {
233
+ const directives = [];
234
+ for (const part of policy.split(";")) {
235
+ const [name, ...values] = part.trim().split(/\s+/).filter(Boolean);
236
+ if (name && !directives.some((d) => d.name === name.toLowerCase())) directives.push({ name: name.toLowerCase(), values });
237
+ }
238
+ return directives;
239
+ }
240
+ var serializePolicy = (directives) => directives.map((d) => [d.name, ...d.values].join(" ")).join("; ");
241
+ var HELMET_DEFAULTS = { "script-src": ["'self'"], "img-src": ["'self'", "data:"] };
242
+ function mergeHelmetDirectives(policy, sources) {
243
+ const keys = Object.keys(policy);
244
+ const camel = keys.length > 0 && keys.every((k) => !k.includes("-"));
245
+ const keyOf = (name) => keys.find((k) => camelToKebab(k) === name);
246
+ const read = /* @__PURE__ */ new Map();
247
+ const valuesOf = (key) => {
248
+ if (key === void 0) return void 0;
249
+ const seen = read.get(key);
250
+ if (seen) return [...seen];
251
+ const value = policy[key];
252
+ const values = typeof value === "string" ? value.split(/\s+/).filter(Boolean) : value != null && typeof value[Symbol.iterator] === "function" ? [...value] : void 0;
253
+ if (values) read.set(key, values);
254
+ return values && [...values];
255
+ };
256
+ const isIterator = (value) => value != null && typeof value.next === "function" && value[Symbol.iterator]?.() === value;
257
+ const shaped = (key, values) => {
258
+ const value = policy[key];
259
+ if (typeof value === "string") return values.join(" ");
260
+ if (value instanceof Set) return new Set(values);
261
+ return isIterator(value) ? values.values() : values;
262
+ };
263
+ const out = { ...policy };
264
+ for (const [name, list] of Object.entries(sources)) {
265
+ if (list.length === 0) continue;
266
+ const ownKey = keyOf(name);
267
+ const own = valuesOf(ownKey);
268
+ const helmetDefault = ownKey !== void 0 && policy[ownKey] === null ? void 0 : HELMET_DEFAULTS[name];
269
+ const seed = own ?? (helmetDefault ? [...helmetDefault] : FALLBACKS[name].map((f) => valuesOf(keyOf(f))).find((v) => v !== void 0)) ?? ["'self'"];
270
+ const merged = addSources(seed, list);
271
+ const key = ownKey ?? (camel ? kebabToCamel(name) : name);
272
+ out[key] = shaped(key, merged);
273
+ if (name === "script-src") {
274
+ const elemKey = keyOf("script-src-elem");
275
+ const elem = valuesOf(elemKey);
276
+ if (elemKey !== void 0 && elem) out[elemKey] = shaped(elemKey, addSources(elem, list));
277
+ }
278
+ }
279
+ for (const [key, values] of read) if (out[key] === policy[key] && isIterator(policy[key])) out[key] = values.values();
280
+ return out;
281
+ }
282
+ var ContentSecurityPolicy = class {
283
+ directives;
284
+ constructor(directives) {
285
+ this.directives = Object.freeze({
286
+ "frame-src": Object.freeze([...directives["frame-src"]]),
287
+ "script-src": Object.freeze([...directives["script-src"]]),
288
+ "connect-src": Object.freeze([...directives["connect-src"]]),
289
+ "img-src": Object.freeze([...directives["img-src"]])
290
+ });
291
+ }
292
+ // A directive with no sources is left out: written bare, it would allow nothing.
293
+ toString() {
294
+ return serializePolicy(
295
+ Object.entries(this.directives).filter(([, values]) => values.length > 0).map(([name, values]) => ({ name, values: [...values] }))
296
+ );
297
+ }
298
+ merge(policy) {
299
+ if (typeof policy !== "string") return mergeHelmetDirectives(policy, this.directives);
300
+ return policy.split(",").map((one) => {
301
+ const directives = parsePolicy(one);
302
+ mergeDirectives(directives, this.directives);
303
+ return serializePolicy(directives);
304
+ }).join(", ");
305
+ }
306
+ applyTo(headers) {
307
+ for (const name of POLICY_HEADERS) {
308
+ const value = headers.get(name);
309
+ if (value !== null) headers.set(name, this.merge(value));
310
+ }
311
+ }
312
+ toHtml(policy) {
313
+ return `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(this.merge(policy))}">`;
314
+ }
315
+ };
316
+ function contentSecurityPolicy(options = {}) {
317
+ const directives = { "frame-src": [ORIGIN], "script-src": [ORIGIN], "connect-src": [apiOrigin(options.environment)], "img-src": [] };
318
+ const environment = environmentOf(options.environment);
319
+ for (const [group, byEnvironment] of Object.entries(PAGE_REQUIREMENTS)) {
320
+ if (options[group] === false) continue;
321
+ for (const [name, list] of Object.entries(byEnvironment[environment].sources)) {
322
+ for (const source of list) if (!directives[name].includes(source)) directives[name].push(source);
323
+ }
324
+ }
325
+ return new ContentSecurityPolicy(directives);
139
326
  }
package/index.d.cts CHANGED
@@ -61,8 +61,71 @@ export type WhopLoad = Promise<WhopConstructor> & { retry(): WhopLoad };
61
61
  /** Load the hosted SDK: injects the `<script data-whop-elements>` tag once and resolves the global constructor. Safe to call any number of times: one script, one shared promise. A failed load is held, so every later call returns the same rejected promise and nothing retries on its own; call `retry()` on it to load again. */
62
62
  export declare function loadWhop(): WhopLoad;
63
63
 
64
- /** Resource hints (preconnect/preload) for the hosted SDK. Render them into `<head>` to start the fetch before `loadWhop()` runs. */
65
- export declare function preloadHints(): { rel: string; href: string; as?: string }[];
64
+ /** One `<link>` hint: render it into `<head>`, or send the list as a `Link` header. */
65
+ export interface PreloadHint {
66
+ rel: string;
67
+ href: string;
68
+ as?: string;
69
+ crossOrigin?: "anonymous";
70
+ }
71
+
72
+ /** Which connections `preloadHints()` warms beyond the hosted SDK. Unknown keys are ignored. */
73
+ export interface PreloadHintsOptions {
74
+ /** Which environment to warm for: the Whop API, and each provider's sandbox hosts in sandbox. Defaults to production. */
75
+ environment?: NonNullable<WhopGlobalConfig["environment"]>;
76
+ /**
77
+ * Apple Pay, Google Pay, and PayPal: preloads its scripts and warms the connections it opens from your page. Pass false on pages that don't show it.
78
+ */
79
+ wallets?: boolean;
80
+ }
81
+
82
+ /** The hints `preloadHints()` returns: an array of {@link PreloadHint}, plus ways to apply it. `filter`, `slice`, `concat`, `toSorted` and `toReversed` keep these methods; `map` returns a plain array. */
83
+ export interface WhopPreloadHints<T extends PreloadHint = PreloadHint> extends Array<T> {
84
+ filter<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: unknown): WhopPreloadHints<S>;
85
+ filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: unknown): WhopPreloadHints<T>;
86
+ slice(start?: number, end?: number): WhopPreloadHints<T>;
87
+ concat(...items: (T | ConcatArray<T>)[]): WhopPreloadHints<T>;
88
+ toSorted(compareFn?: (a: T, b: T) => number): WhopPreloadHints<T>;
89
+ toReversed(): WhopPreloadHints<T>;
90
+ /** The hints as one `Link` header value (servers and CDNs can turn it into 103 Early Hints). */
91
+ toLinkHeader(): string;
92
+ /** The hints as `<link>` tags, for server-rendered templates. */
93
+ toHtml(): string;
94
+ /** Append the hints to a response's `Link` header. */
95
+ applyTo(headers: Headers): void;
96
+ }
97
+
98
+ /** Resource hints (preconnect/preload) for the hosted SDK, the Whop API, and the scripts and connections elements use on your page. Render them into `<head>` to start the fetch before `loadWhop()` runs. */
99
+ export declare function preloadHints(options?: PreloadHintsOptions): WhopPreloadHints;
100
+
101
+ /** Which origins the policy allows. Unknown keys are ignored. */
102
+ export interface ContentSecurityPolicyOptions {
103
+ /** Which environment the policy allows: the Whop API, and each provider's sandbox hosts in sandbox. Defaults to production. */
104
+ environment?: NonNullable<WhopGlobalConfig["environment"]>;
105
+ /**
106
+ * Apple Pay, Google Pay, and PayPal: allows the origins it reaches from your page. Pass false on pages that don't show it.
107
+ */
108
+ wallets?: boolean;
109
+ }
110
+
111
+ /** The Content Security Policy sources elements need on your page. Merge them into your own policy; on their own they block everything else your page loads. */
112
+ export interface WhopContentSecurityPolicy {
113
+ /** The sources elements need, by directive. */
114
+ readonly directives: { readonly "frame-src": readonly string[]; readonly "script-src": readonly string[]; readonly "connect-src": readonly string[]; readonly "img-src": readonly string[] };
115
+ /** Only the sources elements need, as a policy string, leaving out a directive with none. Merge it into your own policy; used alone it blocks everything else. */
116
+ toString(): string;
117
+ /** Your policy with these sources added. A directive you don't set is left alone, except that one falling back to `default-src` starts from those values. */
118
+ merge(policy: string): string;
119
+ /** Your directives object, in helmet's shape, with these sources added. It assumes helmet's defaults (`useDefaults: true`), which fill every directive you leave out, so these directives are always written, starting from your value, else what helmet would send: `'self'` for `script-src`, `'self' data:` for `img-src`, and your `child-src` or `default-src` (else `'self'`) for the others. Other keys are returned as they were. */
120
+ merge<T extends Record<string, unknown>>(directives: T): T;
121
+ /** Merge these sources into the response's `Content-Security-Policy` (and `Content-Security-Policy-Report-Only`) headers. Does nothing to a response that sets neither. */
122
+ applyTo(headers: Headers): void;
123
+ /** Your policy, merged, as a `<meta http-equiv>` tag. A meta tag works only when it is your page's whole policy: the browser also enforces any header policy on top of it. */
124
+ toHtml(policy: string): string;
125
+ }
126
+
127
+ /** The Content Security Policy sources elements need on your page: the hosted frames and SDK, the Whop API, and the origins elements reach from your page. The scripts elements load for Apple Pay, Google Pay, and PayPal come from other companies and can load resources beyond these: check each provider's Content Security Policy guidance. A modal element dims the page behind it with an inline style, which a `style-src` without inline styles leaves out; the modal still works. */
128
+ export declare function contentSecurityPolicy(options?: ContentSecurityPolicyOptions): WhopContentSecurityPolicy;
66
129
 
67
130
  /** The locales this app's elements are built with, the default locale first: exactly the values `locale` accepts. `includes()` accepts any string and narrows it to `WhopElementsLocale`, so use it to validate dynamic values; anything unlisted falls back to the default locale. */
68
131
  export declare const WHOP_ELEMENTS_LOCALES: readonly WhopElementsLocale[] & {
package/index.d.ts CHANGED
@@ -61,8 +61,71 @@ export type WhopLoad = Promise<WhopConstructor> & { retry(): WhopLoad };
61
61
  /** Load the hosted SDK: injects the `<script data-whop-elements>` tag once and resolves the global constructor. Safe to call any number of times: one script, one shared promise. A failed load is held, so every later call returns the same rejected promise and nothing retries on its own; call `retry()` on it to load again. */
62
62
  export declare function loadWhop(): WhopLoad;
63
63
 
64
- /** Resource hints (preconnect/preload) for the hosted SDK. Render them into `<head>` to start the fetch before `loadWhop()` runs. */
65
- export declare function preloadHints(): { rel: string; href: string; as?: string }[];
64
+ /** One `<link>` hint: render it into `<head>`, or send the list as a `Link` header. */
65
+ export interface PreloadHint {
66
+ rel: string;
67
+ href: string;
68
+ as?: string;
69
+ crossOrigin?: "anonymous";
70
+ }
71
+
72
+ /** Which connections `preloadHints()` warms beyond the hosted SDK. Unknown keys are ignored. */
73
+ export interface PreloadHintsOptions {
74
+ /** Which environment to warm for: the Whop API, and each provider's sandbox hosts in sandbox. Defaults to production. */
75
+ environment?: NonNullable<WhopGlobalConfig["environment"]>;
76
+ /**
77
+ * Apple Pay, Google Pay, and PayPal: preloads its scripts and warms the connections it opens from your page. Pass false on pages that don't show it.
78
+ */
79
+ wallets?: boolean;
80
+ }
81
+
82
+ /** The hints `preloadHints()` returns: an array of {@link PreloadHint}, plus ways to apply it. `filter`, `slice`, `concat`, `toSorted` and `toReversed` keep these methods; `map` returns a plain array. */
83
+ export interface WhopPreloadHints<T extends PreloadHint = PreloadHint> extends Array<T> {
84
+ filter<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: unknown): WhopPreloadHints<S>;
85
+ filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: unknown): WhopPreloadHints<T>;
86
+ slice(start?: number, end?: number): WhopPreloadHints<T>;
87
+ concat(...items: (T | ConcatArray<T>)[]): WhopPreloadHints<T>;
88
+ toSorted(compareFn?: (a: T, b: T) => number): WhopPreloadHints<T>;
89
+ toReversed(): WhopPreloadHints<T>;
90
+ /** The hints as one `Link` header value (servers and CDNs can turn it into 103 Early Hints). */
91
+ toLinkHeader(): string;
92
+ /** The hints as `<link>` tags, for server-rendered templates. */
93
+ toHtml(): string;
94
+ /** Append the hints to a response's `Link` header. */
95
+ applyTo(headers: Headers): void;
96
+ }
97
+
98
+ /** Resource hints (preconnect/preload) for the hosted SDK, the Whop API, and the scripts and connections elements use on your page. Render them into `<head>` to start the fetch before `loadWhop()` runs. */
99
+ export declare function preloadHints(options?: PreloadHintsOptions): WhopPreloadHints;
100
+
101
+ /** Which origins the policy allows. Unknown keys are ignored. */
102
+ export interface ContentSecurityPolicyOptions {
103
+ /** Which environment the policy allows: the Whop API, and each provider's sandbox hosts in sandbox. Defaults to production. */
104
+ environment?: NonNullable<WhopGlobalConfig["environment"]>;
105
+ /**
106
+ * Apple Pay, Google Pay, and PayPal: allows the origins it reaches from your page. Pass false on pages that don't show it.
107
+ */
108
+ wallets?: boolean;
109
+ }
110
+
111
+ /** The Content Security Policy sources elements need on your page. Merge them into your own policy; on their own they block everything else your page loads. */
112
+ export interface WhopContentSecurityPolicy {
113
+ /** The sources elements need, by directive. */
114
+ readonly directives: { readonly "frame-src": readonly string[]; readonly "script-src": readonly string[]; readonly "connect-src": readonly string[]; readonly "img-src": readonly string[] };
115
+ /** Only the sources elements need, as a policy string, leaving out a directive with none. Merge it into your own policy; used alone it blocks everything else. */
116
+ toString(): string;
117
+ /** Your policy with these sources added. A directive you don't set is left alone, except that one falling back to `default-src` starts from those values. */
118
+ merge(policy: string): string;
119
+ /** Your directives object, in helmet's shape, with these sources added. It assumes helmet's defaults (`useDefaults: true`), which fill every directive you leave out, so these directives are always written, starting from your value, else what helmet would send: `'self'` for `script-src`, `'self' data:` for `img-src`, and your `child-src` or `default-src` (else `'self'`) for the others. Other keys are returned as they were. */
120
+ merge<T extends Record<string, unknown>>(directives: T): T;
121
+ /** Merge these sources into the response's `Content-Security-Policy` (and `Content-Security-Policy-Report-Only`) headers. Does nothing to a response that sets neither. */
122
+ applyTo(headers: Headers): void;
123
+ /** Your policy, merged, as a `<meta http-equiv>` tag. A meta tag works only when it is your page's whole policy: the browser also enforces any header policy on top of it. */
124
+ toHtml(policy: string): string;
125
+ }
126
+
127
+ /** The Content Security Policy sources elements need on your page: the hosted frames and SDK, the Whop API, and the origins elements reach from your page. The scripts elements load for Apple Pay, Google Pay, and PayPal come from other companies and can load resources beyond these: check each provider's Content Security Policy guidance. A modal element dims the page behind it with an inline style, which a `style-src` without inline styles leaves out; the modal still works. */
128
+ export declare function contentSecurityPolicy(options?: ContentSecurityPolicyOptions): WhopContentSecurityPolicy;
66
129
 
67
130
  /** The locales this app's elements are built with, the default locale first: exactly the values `locale` accepts. `includes()` accepts any string and narrows it to `WhopElementsLocale`, so use it to validate dynamic values; anything unlisted falls back to the default locale. */
68
131
  export declare const WHOP_ELEMENTS_LOCALES: readonly WhopElementsLocale[] & {