@optable/web-sdk 0.45.0 → 0.48.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.
@@ -0,0 +1,146 @@
1
+ const DEFAULT_MAX_CONTENT_LENGTH = 5000;
2
+ const DEFAULT_MAX_HTML_LENGTH = 50000;
3
+ const MAX_HEADINGS = 20;
4
+ function extractSemanticContent(config) {
5
+ var _a;
6
+ const semantic = {
7
+ title: document.title || "",
8
+ };
9
+ // Extract meta description
10
+ const descriptionMeta = document.querySelector('meta[name="description"]');
11
+ if (descriptionMeta) {
12
+ const content = descriptionMeta.getAttribute("content");
13
+ if (content) {
14
+ semantic.description = content;
15
+ }
16
+ }
17
+ // Extract meta keywords
18
+ const keywordsMeta = document.querySelector('meta[name="keywords"]');
19
+ if (keywordsMeta) {
20
+ const content = keywordsMeta.getAttribute("content");
21
+ if (content) {
22
+ semantic.keywords = content
23
+ .split(",")
24
+ .map((k) => k.trim())
25
+ .filter(Boolean);
26
+ }
27
+ }
28
+ // Extract canonical URL
29
+ const canonicalLink = document.querySelector('link[rel="canonical"]');
30
+ if (canonicalLink) {
31
+ const href = canonicalLink.getAttribute("href");
32
+ if (href) {
33
+ semantic.canonicalUrl = href;
34
+ }
35
+ }
36
+ // Extract Open Graph tags
37
+ const ogTags = {};
38
+ document.querySelectorAll('meta[property^="og:"]').forEach((meta) => {
39
+ const property = meta.getAttribute("property");
40
+ const content = meta.getAttribute("content");
41
+ if (property && content) {
42
+ const key = property.replace("og:", "");
43
+ ogTags[key] = content;
44
+ }
45
+ });
46
+ if (Object.keys(ogTags).length > 0) {
47
+ semantic.ogTags = ogTags;
48
+ }
49
+ // Extract headings (h1-h3, max 20)
50
+ const headings = [];
51
+ document.querySelectorAll("h1, h2, h3").forEach((heading) => {
52
+ var _a;
53
+ if (headings.length >= MAX_HEADINGS)
54
+ return;
55
+ const level = parseInt(heading.tagName.substring(1), 10);
56
+ const text = (_a = heading.textContent) === null || _a === void 0 ? void 0 : _a.trim();
57
+ if (text) {
58
+ headings.push({ level, text });
59
+ }
60
+ });
61
+ if (headings.length > 0) {
62
+ semantic.headings = headings;
63
+ }
64
+ // Extract main content
65
+ const maxContentLength = (_a = config.maxContentLength) !== null && _a !== void 0 ? _a : DEFAULT_MAX_CONTENT_LENGTH;
66
+ const contentElement = findContentElement(config.contentSelector);
67
+ if (contentElement) {
68
+ const text = extractTextContent(contentElement);
69
+ if (text) {
70
+ semantic.content = text.substring(0, maxContentLength);
71
+ }
72
+ }
73
+ // Extract JSON-LD
74
+ const jsonLdScripts = document.querySelectorAll('script[type="application/ld+json"]');
75
+ const jsonLdData = [];
76
+ jsonLdScripts.forEach((script) => {
77
+ try {
78
+ const data = JSON.parse(script.textContent || "");
79
+ if (data && typeof data === "object") {
80
+ jsonLdData.push(data);
81
+ }
82
+ }
83
+ catch (_a) {
84
+ // Ignore invalid JSON-LD
85
+ }
86
+ });
87
+ if (jsonLdData.length > 0) {
88
+ semantic.jsonLd = jsonLdData;
89
+ }
90
+ // Extract language
91
+ const lang = document.documentElement.getAttribute("lang");
92
+ if (lang) {
93
+ semantic.language = lang;
94
+ }
95
+ return semantic;
96
+ }
97
+ function findContentElement(selector) {
98
+ // Use provided selector if available
99
+ if (selector) {
100
+ return document.querySelector(selector);
101
+ }
102
+ // Fall back to heuristics: main, article, or first large content block
103
+ const candidates = ["main", "article", '[role="main"]', ".content", "#content", ".post", ".article"];
104
+ for (const candidate of candidates) {
105
+ const element = document.querySelector(candidate);
106
+ if (element) {
107
+ return element;
108
+ }
109
+ }
110
+ // Last resort: body
111
+ return document.body;
112
+ }
113
+ function extractTextContent(element) {
114
+ if (element instanceof HTMLElement && element.innerText) {
115
+ return element.innerText.trim();
116
+ }
117
+ return "";
118
+ }
119
+ function extractContext(config) {
120
+ var _a;
121
+ const contextData = {
122
+ semantic: extractSemanticContent(config),
123
+ url: window.location.href,
124
+ extractedAt: Date.now(),
125
+ };
126
+ // Include referrer if available
127
+ if (document.referrer) {
128
+ contextData.referrer = document.referrer;
129
+ }
130
+ // Include HTML if configured
131
+ if (config.includeHtml) {
132
+ const maxHtmlLength = (_a = config.maxHtmlLength) !== null && _a !== void 0 ? _a : DEFAULT_MAX_HTML_LENGTH;
133
+ contextData.html = document.documentElement.outerHTML.substring(0, maxHtmlLength);
134
+ }
135
+ return contextData;
136
+ }
137
+ function normalizeContextConfig(config) {
138
+ if (!config) {
139
+ return null;
140
+ }
141
+ if (config === true) {
142
+ return {};
143
+ }
144
+ return config;
145
+ }
146
+ export { extractContext, extractSemanticContent, normalizeContextConfig };
@@ -52,6 +52,9 @@ function Targeting(config, req) {
52
52
  if (abTest.skipMatchers) {
53
53
  searchParams.append("skip_matchers", abTest.skipMatchers.join(","));
54
54
  }
55
+ if (abTest.skipResolvers) {
56
+ searchParams.append("skip_resolvers", abTest.skipResolvers.join(","));
57
+ }
55
58
  }
56
59
  if ((_a = config.additionalTargetingSignals) === null || _a === void 0 ? void 0 : _a.ref) {
57
60
  searchParams.append("ref", `${window.location.protocol}//${window.location.host}${window.location.pathname}`);
@@ -1,7 +1,10 @@
1
1
  import type { ResolvedConfig } from "../config";
2
+ import type { ContextData } from "../core/context";
2
3
  type WitnessProperties = {
3
- [key: string]: string | number | boolean;
4
+ [key: string]: string | number | boolean | unknown[] | null | {
5
+ [key: string]: unknown;
6
+ };
4
7
  };
5
- declare function Witness(config: ResolvedConfig, event: string, properties: WitnessProperties): Promise<void>;
8
+ declare function Witness(config: ResolvedConfig, event: string, properties: WitnessProperties, context?: ContextData): Promise<void>;
6
9
  export { Witness, WitnessProperties };
7
10
  export default Witness;
@@ -1,9 +1,12 @@
1
1
  import { fetch } from "../core/network";
2
- function Witness(config, event, properties) {
2
+ function Witness(config, event, properties, context) {
3
3
  const evt = {
4
4
  event: event,
5
5
  properties: properties,
6
6
  };
7
+ if (context) {
8
+ evt.pageContext = context;
9
+ }
7
10
  return fetch("/witness", config, {
8
11
  method: "POST",
9
12
  headers: {
package/lib/dist/sdk.d.ts CHANGED
@@ -10,6 +10,8 @@ declare class OptableSDK {
10
10
  static version: string;
11
11
  dcn: ResolvedConfig;
12
12
  protected init: Promise<void>;
13
+ private contextSent;
14
+ private contextConfig;
13
15
  constructor(dcn: InitConfig);
14
16
  initialize(): Promise<void>;
15
17
  identify(...ids: string[]): Promise<void>;
@@ -23,7 +25,10 @@ declare class OptableSDK {
23
25
  prebidORTB2FromCache(): PrebidORTB2;
24
26
  targetingKeyValues(): Promise<TargetingKeyValues>;
25
27
  targetingKeyValuesFromCache(): TargetingKeyValues;
26
- witness(event: string, properties?: WitnessProperties): Promise<void>;
28
+ witness(event: string, properties?: WitnessProperties, options?: {
29
+ includeContext?: boolean;
30
+ }): Promise<void>;
31
+ resetContext(): void;
27
32
  profile(traits: ProfileTraits, id?: string | null, neighbors?: string[] | null): Promise<void>;
28
33
  tokenize(id: string): Promise<TokenizeResponse>;
29
34
  resolve(id?: string): Promise<ResolveResponse>;
package/lib/dist/sdk.js CHANGED
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { default as buildInfo } from "./build.json";
11
11
  import { getConfig } from "./config";
12
+ import { extractContext, normalizeContextConfig } from "./core/context";
12
13
  import { Identify } from "./edge/identify";
13
14
  import { Uid2Token } from "./edge/uid2_token";
14
15
  import { Resolve } from "./edge/resolve";
@@ -21,7 +22,10 @@ import { sha256 } from "js-sha256";
21
22
  import { Tokenize } from "./edge/tokenize";
22
23
  class OptableSDK {
23
24
  constructor(dcn) {
25
+ this.contextSent = false;
26
+ this.contextConfig = null;
24
27
  this.dcn = getConfig(dcn);
28
+ this.contextConfig = normalizeContextConfig(dcn.pageContext);
25
29
  this.init = this.initialize();
26
30
  }
27
31
  initialize() {
@@ -86,11 +90,19 @@ class OptableSDK {
86
90
  return TargetingKeyValues(tdata);
87
91
  }
88
92
  witness(event_1) {
89
- return __awaiter(this, arguments, void 0, function* (event, properties = {}) {
93
+ return __awaiter(this, arguments, void 0, function* (event, properties = {}, options = {}) {
90
94
  yield this.init;
91
- return Witness(this.dcn, event, properties);
95
+ let context;
96
+ if (options.includeContext && this.contextConfig && !this.contextSent) {
97
+ context = extractContext(this.contextConfig);
98
+ this.contextSent = true;
99
+ }
100
+ return Witness(this.dcn, event, properties, context);
92
101
  });
93
102
  }
103
+ resetContext() {
104
+ this.contextSent = false;
105
+ }
94
106
  profile(traits_1) {
95
107
  return __awaiter(this, arguments, void 0, function* (traits, id = null, neighbors = null) {
96
108
  yield this.init;
package/package.json CHANGED
@@ -5,11 +5,11 @@
5
5
  "browser": "./lib/dist/sdk.js",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/optable/optable-web-sdk.git"
8
+ "url": "git+https://github.com/Optable/optable-web-sdk.git"
9
9
  },
10
10
  "homepage": "https://optable.co",
11
11
  "license": "SEE LICENSE IN LICENSE",
12
- "version": "v0.45.0",
12
+ "version": "v0.48.0",
13
13
  "devDependencies": {
14
14
  "@babel/core": "^7.12.3",
15
15
  "@babel/plugin-proposal-class-properties": "^7.12.1",
@@ -18,6 +18,7 @@
18
18
  "@babel/preset-typescript": "^7.12.1",
19
19
  "@types/googletag": "^3.1.3",
20
20
  "@types/jest": "^26.0.15",
21
+ "@types/node": "^25.0.3",
21
22
  "babel-jest": "^29.7.0",
22
23
  "babel-loader": "^8.2.2",
23
24
  "core-js": "^3.7.0",
@@ -28,13 +29,15 @@
28
29
  "prettier": "^3.6.2",
29
30
  "shellcheck": "^3.0.0",
30
31
  "typescript": "^5.2.2",
31
- "webpack": "^5.97.1",
32
+ "webpack": "^5.104.1",
32
33
  "webpack-cli": "^5.1.4",
33
34
  "whatwg-fetch": "^3.6.20"
34
35
  },
35
36
  "dependencies": {
36
37
  "@babel/runtime": "^7.27.0",
37
38
  "@optable/web-sdk": "^0.40.0",
39
+ "bowser": "^2.12.1",
40
+ "iab-adcom": "^1.0.6",
38
41
  "iab-openrtb": "^1.0.1",
39
42
  "js-sha256": "^0.11.0",
40
43
  "regenerator-runtime": "^0.13.7"
@@ -47,12 +50,12 @@
47
50
  "LICENSE"
48
51
  ],
49
52
  "scripts": {
50
- "build": "npm run build-lib && npm run build-web",
53
+ "build": "pnpm build-lib && pnpm build-web",
51
54
  "start": "tsc -b browser --watch & webpack --config=./browser/webpack.config.js --mode=development --watch --devtool=source-map",
52
55
  "build-lib": "tsc -b lib",
53
56
  "build-web": "tsc -b browser && webpack --config=./browser/webpack.config.js",
54
57
  "test": "jest",
55
58
  "format": "prettier . --write",
56
- "lint-scripts": "npx shellcheck scripts/*.sh"
59
+ "lint-scripts": "pnpm exec shellcheck scripts/*.sh"
57
60
  }
58
- }
61
+ }