@cunpingtai/webad-react-ad-scripts 0.1.0 → 0.2.1

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/dist/index.js CHANGED
@@ -14528,6 +14528,73 @@ function date4(params) {
14528
14528
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
14529
14529
  config(en_default());
14530
14530
 
14531
+ // ../shared-contract/src/adsterra.ts
14532
+ var PLACEMENT_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
14533
+ var CONTAINER_ID = /^[A-Za-z][A-Za-z0-9:_-]{0,127}$/;
14534
+ var ZONE_KEY = /^[A-Za-z0-9]{1,128}$/;
14535
+ var PARAM_KEY = /^[A-Za-z0-9_-]{1,64}$/;
14536
+ var MAX_URL_LENGTH = 2048;
14537
+ var AdsterraValueSchema = external_exports.union([
14538
+ external_exports.string().max(256),
14539
+ external_exports.number().finite(),
14540
+ external_exports.boolean()
14541
+ ]);
14542
+ var AdsterraParamsSchema = external_exports.record(external_exports.string().regex(PARAM_KEY), AdsterraValueSchema).refine((params) => Object.keys(params).length <= 16, {
14543
+ message: "Adsterra params must contain at most 16 entries"
14544
+ });
14545
+ var ManagedAdsterraUrlSchema = external_exports.string().min(1).max(MAX_URL_LENGTH).refine(isSafeManagedUrl, { message: "Invalid managed Adsterra URL" });
14546
+ var AdsterraBannerPlacementSchema = external_exports.object({
14547
+ kind: external_exports.literal("banner"),
14548
+ scriptSrc: ManagedAdsterraUrlSchema,
14549
+ zoneKey: external_exports.string().regex(ZONE_KEY),
14550
+ width: external_exports.number().int().min(1).max(2048),
14551
+ height: external_exports.number().int().min(1).max(2048),
14552
+ params: AdsterraParamsSchema
14553
+ }).strict();
14554
+ var AdsterraNativePlacementSchema = external_exports.object({
14555
+ kind: external_exports.literal("native"),
14556
+ scriptSrc: ManagedAdsterraUrlSchema,
14557
+ containerId: external_exports.string().regex(CONTAINER_ID)
14558
+ }).strict();
14559
+ var AdsterraPlacementSchema = external_exports.discriminatedUnion("kind", [
14560
+ AdsterraBannerPlacementSchema,
14561
+ AdsterraNativePlacementSchema
14562
+ ]);
14563
+ var AdsterraPlacementsSchema = external_exports.record(
14564
+ external_exports.string().min(1).max(80).regex(PLACEMENT_NAME),
14565
+ AdsterraPlacementSchema
14566
+ ).refine((placements) => Object.keys(placements).length <= 64, {
14567
+ message: "A site may contain at most 64 Adsterra placements"
14568
+ });
14569
+ var ManagedScriptSchema = external_exports.object({ scriptSrc: ManagedAdsterraUrlSchema }).strict();
14570
+ var ManagedSmartlinkSchema = external_exports.object({ href: ManagedAdsterraUrlSchema }).strict();
14571
+ var SiteAdsterraConfigSchema = external_exports.object({
14572
+ placements: AdsterraPlacementsSchema,
14573
+ socialBar: ManagedScriptSchema.optional(),
14574
+ popunder: ManagedScriptSchema.optional(),
14575
+ smartlink: ManagedSmartlinkSchema.optional()
14576
+ }).strict();
14577
+ function isSafeManagedUrl(value) {
14578
+ if (/\s/.test(value) || value.includes("#") || hasExplicitPort(value)) {
14579
+ return false;
14580
+ }
14581
+ try {
14582
+ const url2 = new URL(value);
14583
+ return url2.protocol === "https:" && url2.username === "" && url2.password === "" && url2.port === "" && url2.hash === "";
14584
+ } catch {
14585
+ return false;
14586
+ }
14587
+ }
14588
+ function hasExplicitPort(value) {
14589
+ const schemeEnd = value.indexOf(":");
14590
+ if (schemeEnd < 0) return false;
14591
+ const remainder = value.slice(schemeEnd + 1).replace(/^[\\/]+/, "");
14592
+ const authorityEnd = remainder.search(/[\\/?#]/);
14593
+ const authority = authorityEnd < 0 ? remainder : remainder.slice(0, authorityEnd);
14594
+ const host = authority.slice(authority.lastIndexOf("@") + 1);
14595
+ return host.startsWith("[") ? host.includes("]:") : host.includes(":");
14596
+ }
14597
+
14531
14598
  // ../shared-contract/src/schemas.ts
14532
14599
  var ScriptDescriptorSchema = external_exports.object({
14533
14600
  id: external_exports.string().min(1).max(80),
@@ -14565,7 +14632,8 @@ var SiteConfigSchema = external_exports.object({
14565
14632
  enabled: external_exports.boolean(),
14566
14633
  mode: external_exports.union([external_exports.enum(AD_MODES), external_exports.literal("inherit")]),
14567
14634
  aliases: external_exports.array(external_exports.string()).max(32),
14568
- providers: ProviderOverridesSchema
14635
+ providers: ProviderOverridesSchema,
14636
+ adsterra: SiteAdsterraConfigSchema.optional()
14569
14637
  }).strict();
14570
14638
  var SnapshotSchema = external_exports.object({
14571
14639
  schemaVersion: external_exports.literal(1),
@@ -14584,7 +14652,8 @@ var PublicConfigSchema = external_exports.object({
14584
14652
  mode: external_exports.enum(AD_MODES),
14585
14653
  allowedScriptHosts: external_exports.array(external_exports.string().min(1).max(253)).max(64),
14586
14654
  scripts: external_exports.array(ScriptDescriptorSchema).max(32),
14587
- ttlSeconds: external_exports.number().int().min(0).max(300)
14655
+ ttlSeconds: external_exports.number().int().min(0).max(300),
14656
+ adsterra: SiteAdsterraConfigSchema.optional()
14588
14657
  }).strict();
14589
14658
 
14590
14659
  // ../shared-contract/src/hostname.ts
@@ -14775,10 +14844,10 @@ function requestConfig(endpoint, host, timeoutMs) {
14775
14844
 
14776
14845
  // src/validation.ts
14777
14846
  var SLOT_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
14778
- var CONTAINER_ID = /^[A-Za-z][A-Za-z0-9:_-]{0,127}$/;
14779
- var ZONE_KEY = /^[A-Za-z0-9]{1,128}$/;
14780
- var PARAM_KEY = /^[A-Za-z0-9_-]{1,64}$/;
14781
- var MAX_URL_LENGTH = 2048;
14847
+ var CONTAINER_ID2 = /^[A-Za-z][A-Za-z0-9:_-]{0,127}$/;
14848
+ var ZONE_KEY2 = /^[A-Za-z0-9]{1,128}$/;
14849
+ var PARAM_KEY2 = /^[A-Za-z0-9_-]{1,64}$/;
14850
+ var MAX_URL_LENGTH2 = 2048;
14782
14851
  function validatePageKey(value) {
14783
14852
  if (typeof value !== "string" || value.length < 1 || value.length > 2048) {
14784
14853
  throw new Error("invalid pageKey");
@@ -14791,13 +14860,13 @@ function validateSlotId(value) {
14791
14860
  }
14792
14861
  return value;
14793
14862
  }
14794
- function validateScriptUrl(value, allowedHosts) {
14863
+ function validateComponentScriptUrl(value) {
14795
14864
  const url2 = parseAbsoluteUrl(value, "scriptSrc");
14796
14865
  if (url2.protocol !== "https:") {
14797
14866
  throw new Error("scriptSrc must use HTTPS");
14798
14867
  }
14799
- if (!allowedHosts.some((host) => host.toLowerCase() === url2.hostname)) {
14800
- throw new Error("scriptSrc hostname is not allowed");
14868
+ if (/[\t\n\r]/.test(value) || value.includes("#") || hasExplicitPort2(value)) {
14869
+ throw new Error("invalid scriptSrc");
14801
14870
  }
14802
14871
  return url2.toString();
14803
14872
  }
@@ -14809,13 +14878,13 @@ function validateSmartlinkUrl(value) {
14809
14878
  return url2.toString();
14810
14879
  }
14811
14880
  function validateContainerId(value) {
14812
- if (typeof value !== "string" || !CONTAINER_ID.test(value)) {
14881
+ if (typeof value !== "string" || !CONTAINER_ID2.test(value)) {
14813
14882
  throw new Error("invalid containerId");
14814
14883
  }
14815
14884
  return value;
14816
14885
  }
14817
14886
  function validateZoneKey(value) {
14818
- if (typeof value !== "string" || !ZONE_KEY.test(value)) {
14887
+ if (typeof value !== "string" || !ZONE_KEY2.test(value)) {
14819
14888
  throw new Error("invalid zoneKey");
14820
14889
  }
14821
14890
  return value;
@@ -14838,7 +14907,7 @@ function validateParams(params) {
14838
14907
  throw new Error("invalid params");
14839
14908
  }
14840
14909
  for (const [key, value] of entries) {
14841
- if (!PARAM_KEY.test(key)) {
14910
+ if (!PARAM_KEY2.test(key)) {
14842
14911
  throw new Error("invalid params");
14843
14912
  }
14844
14913
  if (typeof value === "string") {
@@ -14860,7 +14929,7 @@ function validateParams(params) {
14860
14929
  return { ...params };
14861
14930
  }
14862
14931
  function parseAbsoluteUrl(value, name) {
14863
- if (typeof value !== "string" || value.length < 1 || value.length > MAX_URL_LENGTH) {
14932
+ if (typeof value !== "string" || value.length < 1 || value.length > MAX_URL_LENGTH2) {
14864
14933
  throw new Error(`invalid ${name}`);
14865
14934
  }
14866
14935
  let url2;
@@ -14874,6 +14943,21 @@ function parseAbsoluteUrl(value, name) {
14874
14943
  }
14875
14944
  return url2;
14876
14945
  }
14946
+ function hasExplicitPort2(value) {
14947
+ const source = value.trim();
14948
+ const schemeEnd = source.indexOf(":");
14949
+ if (schemeEnd < 0) {
14950
+ return false;
14951
+ }
14952
+ const remainder = source.slice(schemeEnd + 1).replace(/^[\\/]+/, "");
14953
+ const authorityEnd = remainder.search(/[\\/?#]/);
14954
+ const authority = authorityEnd < 0 ? remainder : remainder.slice(0, authorityEnd);
14955
+ const host = authority.slice(authority.lastIndexOf("@") + 1);
14956
+ if (host.startsWith("[")) {
14957
+ return host.includes("]:");
14958
+ }
14959
+ return host.includes(":");
14960
+ }
14877
14961
  function validDimension(value) {
14878
14962
  return Number.isSafeInteger(value) && value >= 1 && value <= 2048;
14879
14963
  }
@@ -14919,6 +15003,13 @@ function AdsProvider({
14919
15003
  mounted = false;
14920
15004
  };
14921
15005
  }
15006
+ if (isLocalHostname(window.location.hostname)) {
15007
+ setPlan(null);
15008
+ emit({ state: "disabled", mode: "none" });
15009
+ return () => {
15010
+ mounted = false;
15011
+ };
15012
+ }
14922
15013
  const load = async () => {
14923
15014
  setPlan(null);
14924
15015
  emit({ state: "loading-config" });
@@ -14983,6 +15074,10 @@ function AdsProvider({
14983
15074
  }, [nonce, plan, providerId, validatedPageKey]);
14984
15075
  return /* @__PURE__ */ jsx(AdsContext.Provider, { value: context, children });
14985
15076
  }
15077
+ function isLocalHostname(hostname3) {
15078
+ const normalized = hostname3.toLowerCase();
15079
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "127.0.0.1" || normalized === "[::1]" || normalized === "::1";
15080
+ }
14986
15081
 
14987
15082
  // src/AdsterraBanner.tsx
14988
15083
  import { useEffect as useEffect2, useRef as useRef2 } from "react";
@@ -15051,12 +15146,7 @@ function isFormatAllowed(mode, format) {
15051
15146
  // src/AdsterraBanner.tsx
15052
15147
  import { jsx as jsx2 } from "react/jsx-runtime";
15053
15148
  function AdsterraBanner({
15054
- slotId,
15055
- scriptSrc,
15056
- zoneKey,
15057
- width,
15058
- height,
15059
- params,
15149
+ placement,
15060
15150
  className,
15061
15151
  style
15062
15152
  }) {
@@ -15065,14 +15155,17 @@ function AdsterraBanner({
15065
15155
  let validated;
15066
15156
  if (context !== null && isFormatAllowed(context.plan.mode, "banner")) {
15067
15157
  try {
15068
- validateSlotId(slotId);
15069
- const dimensions = validateDimensions(width, height);
15070
- validated = {
15071
- src: validateScriptUrl(scriptSrc, context.plan.allowedScriptHosts),
15072
- zoneKey: validateZoneKey(zoneKey),
15073
- ...dimensions,
15074
- params: validateParams(params)
15075
- };
15158
+ validateSlotId(placement);
15159
+ const managed = context.plan.adsterra?.placements[placement];
15160
+ if (managed?.kind === "banner") {
15161
+ const dimensions = validateDimensions(managed.width, managed.height);
15162
+ validated = {
15163
+ src: validateComponentScriptUrl(managed.scriptSrc),
15164
+ zoneKey: validateZoneKey(managed.zoneKey),
15165
+ ...dimensions,
15166
+ params: validateParams(managed.params)
15167
+ };
15168
+ }
15076
15169
  } catch {
15077
15170
  validated = void 0;
15078
15171
  }
@@ -15082,7 +15175,12 @@ function AdsterraBanner({
15082
15175
  if (context === null || validated === void 0 || container === null) {
15083
15176
  return;
15084
15177
  }
15085
- if (!claimPageSlot(document, context.providerId, context.pageViewId, slotId)) {
15178
+ if (!claimPageSlot(
15179
+ document,
15180
+ context.providerId,
15181
+ context.pageViewId,
15182
+ placement
15183
+ )) {
15086
15184
  return;
15087
15185
  }
15088
15186
  void enqueueBanner(document, async () => {
@@ -15103,7 +15201,7 @@ function AdsterraBanner({
15103
15201
  await appendBannerScript(
15104
15202
  container,
15105
15203
  validated.src,
15106
- slotId,
15204
+ placement,
15107
15205
  context.nonce
15108
15206
  );
15109
15207
  } finally {
@@ -15114,7 +15212,7 @@ function AdsterraBanner({
15114
15212
  }
15115
15213
  }
15116
15214
  }).catch(() => void 0);
15117
- }, [context, slotId, validated]);
15215
+ }, [context, placement, validated]);
15118
15216
  if (validated === void 0) {
15119
15217
  return null;
15120
15218
  }
@@ -15122,7 +15220,7 @@ function AdsterraBanner({
15122
15220
  "div",
15123
15221
  {
15124
15222
  ref: containerRef,
15125
- "data-webad-banner-slot": slotId,
15223
+ "data-webad-banner-slot": placement,
15126
15224
  className,
15127
15225
  style: { ...style, width: validated.width, minHeight: validated.height }
15128
15226
  }
@@ -15152,9 +15250,7 @@ function appendBannerScript(container, src, slotId, nonce) {
15152
15250
  import { useEffect as useEffect3, useRef as useRef3 } from "react";
15153
15251
  import { jsx as jsx3 } from "react/jsx-runtime";
15154
15252
  function AdsterraNativeBanner({
15155
- slotId,
15156
- scriptSrc,
15157
- containerId,
15253
+ placement,
15158
15254
  className,
15159
15255
  style
15160
15256
  }) {
@@ -15164,12 +15260,12 @@ function AdsterraNativeBanner({
15164
15260
  let validatedContainerId = null;
15165
15261
  if (context !== null && isFormatAllowed(context.plan.mode, "native-banner")) {
15166
15262
  try {
15167
- validateSlotId(slotId);
15168
- validatedContainerId = validateContainerId(containerId);
15169
- validatedSrc = validateScriptUrl(
15170
- scriptSrc,
15171
- context.plan.allowedScriptHosts
15172
- );
15263
+ validateSlotId(placement);
15264
+ const managed = context.plan.adsterra?.placements[placement];
15265
+ if (managed?.kind === "native") {
15266
+ validatedContainerId = validateContainerId(managed.containerId);
15267
+ validatedSrc = validateComponentScriptUrl(managed.scriptSrc);
15268
+ }
15173
15269
  } catch {
15174
15270
  validatedSrc = null;
15175
15271
  validatedContainerId = null;
@@ -15180,19 +15276,24 @@ function AdsterraNativeBanner({
15180
15276
  if (context === null || validatedSrc === null || validatedContainerId === null || container === null || document.getElementById(validatedContainerId) !== container) {
15181
15277
  return;
15182
15278
  }
15183
- if (!claimPageSlot(document, context.providerId, context.pageViewId, slotId)) {
15279
+ if (!claimPageSlot(
15280
+ document,
15281
+ context.providerId,
15282
+ context.pageViewId,
15283
+ placement
15284
+ )) {
15184
15285
  return;
15185
15286
  }
15186
15287
  const script = document.createElement("script");
15187
15288
  script.src = validatedSrc;
15188
15289
  script.async = true;
15189
15290
  script.dataset.cfasync = "false";
15190
- script.dataset.webadSlotId = slotId;
15291
+ script.dataset.webadSlotId = placement;
15191
15292
  if (context.nonce !== void 0) {
15192
15293
  script.nonce = context.nonce;
15193
15294
  }
15194
15295
  container.append(script);
15195
- }, [context, slotId, validatedContainerId, validatedSrc]);
15296
+ }, [context, placement, validatedContainerId, validatedSrc]);
15196
15297
  if (validatedSrc === null || validatedContainerId === null) {
15197
15298
  return null;
15198
15299
  }
@@ -15210,7 +15311,6 @@ function AdsterraNativeBanner({
15210
15311
  // src/AdsterraSmartlink.tsx
15211
15312
  import { jsx as jsx4 } from "react/jsx-runtime";
15212
15313
  function AdsterraSmartlink({
15213
- href,
15214
15314
  children,
15215
15315
  target = "_blank",
15216
15316
  className
@@ -15221,6 +15321,8 @@ function AdsterraSmartlink({
15221
15321
  }
15222
15322
  let validatedHref;
15223
15323
  try {
15324
+ const href = context.plan.adsterra?.smartlink?.href;
15325
+ if (href === void 0) return null;
15224
15326
  validatedHref = validateSmartlinkUrl(href);
15225
15327
  } catch {
15226
15328
  return null;
@@ -15240,25 +15342,24 @@ function AdsterraSmartlink({
15240
15342
  // src/page-scripts.tsx
15241
15343
  import { useEffect as useEffect4 } from "react";
15242
15344
  import { jsx as jsx5 } from "react/jsx-runtime";
15243
- function AdsterraSocialBar(props) {
15244
- return /* @__PURE__ */ jsx5(PageScript, { ...props, format: "social-bar", target: "body" });
15345
+ function AdsterraSocialBar(_props) {
15346
+ return /* @__PURE__ */ jsx5(PageScript, { format: "social-bar", target: "body" });
15245
15347
  }
15246
- function AdsterraPopunder(props) {
15247
- return /* @__PURE__ */ jsx5(PageScript, { ...props, format: "popunder", target: "head" });
15348
+ function AdsterraPopunder(_props) {
15349
+ return /* @__PURE__ */ jsx5(PageScript, { format: "popunder", target: "head" });
15248
15350
  }
15249
- function PageScript({ format, slotId, scriptSrc, target }) {
15351
+ function PageScript({ format, target }) {
15250
15352
  const context = useAdsContext();
15353
+ const slotId = format === "social-bar" ? "global-social-bar" : "global-popunder";
15251
15354
  useEffect4(() => {
15252
15355
  if (context === null || !isFormatAllowed(context.plan.mode, format)) {
15253
15356
  return;
15254
15357
  }
15255
15358
  let validatedSrc;
15256
15359
  try {
15257
- validateSlotId(slotId);
15258
- validatedSrc = validateScriptUrl(
15259
- scriptSrc,
15260
- context.plan.allowedScriptHosts
15261
- );
15360
+ const managed = format === "social-bar" ? context.plan.adsterra?.socialBar : context.plan.adsterra?.popunder;
15361
+ if (managed === void 0) return;
15362
+ validatedSrc = validateComponentScriptUrl(managed.scriptSrc);
15262
15363
  } catch {
15263
15364
  return;
15264
15365
  }
@@ -15274,7 +15375,7 @@ function PageScript({ format, slotId, scriptSrc, target }) {
15274
15375
  script.nonce = context.nonce;
15275
15376
  }
15276
15377
  document[target].append(script);
15277
- }, [context, format, scriptSrc, slotId, target]);
15378
+ }, [context, format, slotId, target]);
15278
15379
  return null;
15279
15380
  }
15280
15381
  export {