Skip to content

Commit 1def934

Browse files
Bosko MilekicBosko Milekic
authored andcommitted
feat: add ctxSegments() and ctxTargetingKeyValues() for contextual targeting
Introduce a contextual-classification path in the SDK: fetch the DCN's taxonomy/category classifications for a page URL, cache them on the SDK instance, and expose a helper that turns the cached response into GAM-ready targeting key-values. SDK (lib/sdk.ts, lib/edge/contextual_segments.ts): - New OptableSDK.ctxSegments(url?) — POSTs to /v1beta1/contextual and returns ContextualSegmentsResponse { classifications: { categories: [ { taxonomy, id, score, ... } ] } }. url defaults to window.location.href. The contextual request fires immediately (does not await SDK init), so the cache populates as early as possible during initContextual: true startup. Response is cached on the instance; calling again refreshes the cache. Pass-through behavior (no validation / normalization), matching Targeting() at the edge layer. - initContextual: true now also fires ctxSegments() at init time (fire-and-forget, alongside the pageview witness), so the cache is populated automatically before any consumer reads it. - New OptableSDK.ctxTargetingKeyValues(taxonomyKeys?) reads the cached response and returns Record<string, string[]> of category ids grouped by taxonomy, ready for googletag.pubads().setTargeting(). Without a map, keys are the raw taxonomy values; with a map, keys are renamed and only the listed taxonomies are emitted (filter + rename). Ids are deduped, order preserved. Tests (lib/sdk.test.ts, lib/test/handlers.ts): - MSW handler for /v1beta1/contextual. - Breaking-change tests for ctxSegments and ctxTargetingKeyValues. - Behavioral coverage: happy path, window.location.href default, empty / missing / malformed responses, categories spanning multiple taxonomies, cache population, default keying, rename, filter of unmapped taxonomies, and that initContextual: true triggers a contextual request. Docs & demos (README.md, demos/**): - Document the Contextual Segments API in README: ctxSegments() caching semantics, the initContextual precondition for ctxTargetingKeyValues, the explicit ctxSegments().then(loadGAM) alternative, key rename/allow-list, and the requirement that the URL be classified by the DCN. - Two new vanilla demo pages — demos/vanilla/targeting/ctx_segments.html.tpl and demos/vanilla/nocookies/targeting/ctx_segments.html.tpl — that call ctxSegments(), render the categories grouped by taxonomy with score bars, show the derived GAM key-values, and display the raw JSON response. - Register both templates in the Makefile demo-html target and link them from the Audience Targeting section of demos/index.html and demos/index-nocookies.html.
1 parent 684cb4f commit 1def934

10 files changed

Lines changed: 1063 additions & 1 deletion

File tree

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ demo-html:
5252
envsubst $(DEMO_VARS) < demos/vanilla/targeting/gam360-cached.html.tpl > demos/vanilla/targeting/gam360-cached.html
5353
envsubst $(DEMO_VARS) < demos/vanilla/targeting/gam360-adcp.html.tpl > demos/vanilla/targeting/gam360-adcp.html
5454
envsubst $(DEMO_VARS) < demos/vanilla/targeting/prebid.html.tpl > demos/vanilla/targeting/prebid.html
55+
envsubst $(DEMO_VARS) < demos/vanilla/targeting/ctx_segments.html.tpl > demos/vanilla/targeting/ctx_segments.html
5556
envsubst $(DEMO_VARS) < demos/vanilla/nocookies/identify.html.tpl > demos/vanilla/nocookies/identify.html
5657
envsubst $(DEMO_VARS) < demos/vanilla/nocookies/witness.html.tpl > demos/vanilla/nocookies/witness.html
5758
envsubst $(DEMO_VARS) < demos/vanilla/nocookies/profile.html.tpl > demos/vanilla/nocookies/profile.html
5859
envsubst $(DEMO_VARS) < demos/vanilla/nocookies/targeting/gam360.html.tpl > demos/vanilla/nocookies/targeting/gam360.html
5960
envsubst $(DEMO_VARS) < demos/vanilla/nocookies/targeting/gam360-cached.html.tpl > demos/vanilla/nocookies/targeting/gam360-cached.html
6061
envsubst $(DEMO_VARS) < demos/vanilla/nocookies/targeting/gam360-adcp.html.tpl > demos/vanilla/nocookies/targeting/gam360-adcp.html
6162
envsubst $(DEMO_VARS) < demos/vanilla/nocookies/targeting/prebid.html.tpl > demos/vanilla/nocookies/targeting/prebid.html
63+
envsubst $(DEMO_VARS) < demos/vanilla/nocookies/targeting/ctx_segments.html.tpl > demos/vanilla/nocookies/targeting/ctx_segments.html
6264
envsubst $(DEMO_VARS) < demos/vanilla/uid2_token/login.html.tpl > demos/vanilla/uid2_token/login.html
6365
envsubst $(DEMO_VARS) < demos/vanilla/uid2_token/index.html.tpl > demos/vanilla/uid2_token/index.html
6466
envsubst $(DEMO_VARS) < demos/vanilla/pair/index.html.tpl > demos/vanilla/pair/index.html

README.md

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ JavaScript SDK for integrating with an [Optable Data Connectivity Node (DCN)](ht
2525
- [Caching Targeting Data](#caching-targeting-data)
2626
- [Witness API](#witness-api)
2727
- [Contextual Pageview Tracking](#contextual-pageview-tracking)
28+
- [Contextual Segments API](#contextual-segments-api)
29+
- [Contextual targeting key-values](#contextual-targeting-key-values)
2830
- [Using a script tag](#using-a-script-tag)
2931
- [Option 1: Automatic Initialization](#option-1-automatic-initialization)
3032
- [Option 2: Manual Initialization with Commands Queue](#option-2-manual-initialization-with-commands-queue)
@@ -148,7 +150,7 @@ When creating an instance of `OptableSDK`, you can pass an `InitConfig` object t
148150
When set, enables page context extraction for contextual intelligence. Set to `true` to use defaults, or pass a `PageContextConfig` object to customize what is extracted (HTML content, content selector, max lengths). Extracted context is automatically attached to the first `witness()` call that uses `{ includeContext: true }`.
149151

150152
- **`initContextual` (boolean, default: `false`)**
151-
If `true`, the SDK will automatically fire a `pageview` witness event with full page context during initialization. This is the recommended way to enable contextual pageview tracking without writing custom code. Implies `pageContext: true` when no `pageContext` is explicitly configured.
153+
If `true`, the SDK will automatically fire a `pageview` witness event with full page context during initialization, and also call `ctxSegments()` to fetch contextual segments for the current page, caching the result on the instance for later use via `ctxTargetingKeyValues()`. This is the recommended way to enable contextual pageview tracking and contextual targeting without writing custom code. Implies `pageContext: true` when no `pageContext` is explicitly configured.
152154

153155
- **`consent` (`InitConsent`)**
154156
Defines the consent settings for data collection and processing.
@@ -404,6 +406,91 @@ sdk.witness("pageview", { url }, { includeContext: true });
404406

405407
To reset the context (e.g. on SPA navigation), call `sdk.resetContext()` before the next `witness()` call.
406408

409+
### Contextual Segments API
410+
411+
In addition to pageview tracking, the SDK can classify a page URL against one or more contextual taxonomies (such as the [IAB Content Taxonomy](https://iabtechlab.com/standards/content-taxonomy/)) and use the result for ad targeting. Call `ctxSegments()` to fetch the contextual classifications for a URL:
412+
413+
```javascript
414+
// Classify the current page (defaults to window.location.href):
415+
const response = await sdk.ctxSegments();
416+
417+
// Or classify an explicit URL:
418+
const response = await sdk.ctxSegments("https://example.com/article");
419+
```
420+
421+
The response has the shape:
422+
423+
```typescript
424+
type ContextualSegmentsResponse = {
425+
classifications: {
426+
categories: { id: string; name: string; score: number; taxonomy: string }[];
427+
};
428+
};
429+
```
430+
431+
Each call to `ctxSegments()` caches its response on the SDK instance (calling it again refreshes the cache). When `initContextual: true`, the SDK calls `ctxSegments()` for you during initialization, so the cache is populated automatically.
432+
433+
> **Note:** The requested URL must already have been classified by the DCN. If the DCN has no classification for the URL, the response will contain an empty `categories` array.
434+
435+
#### Contextual targeting key-values
436+
437+
`ctxTargetingKeyValues(taxonomyKeys?)` reads the cached `ctxSegments()` response and builds a `Record<string, string[]>` of category ids grouped by taxonomy, ready to pass to an ad server such as Google Ad Manager via `googletag.pubads().setTargeting()`.
438+
439+
Without arguments, each taxonomy value is used as the key:
440+
441+
```javascript
442+
sdk.ctxTargetingKeyValues();
443+
// => { "iab_ct_3_1": ["53", "91", "58", "115", "90", "52"] }
444+
```
445+
446+
Pass a `taxonomyKeys` map to rename keys. Only taxonomies present in the map are emitted (filter + rename), which is useful when you only want to set keys you have configured in your ad server:
447+
448+
```javascript
449+
sdk.ctxTargetingKeyValues({ iab_ct_3_1: "foo" });
450+
// => { "foo": ["53", "91", "58", "115", "90", "52"] }
451+
```
452+
453+
A typical Google Ad Manager activation uses a `loadGAM()` helper:
454+
455+
```javascript
456+
// Helper to load GAM ads with optional targeting data:
457+
var loadGAM = function (tdata = {}) {
458+
window.googletag = window.googletag || { cmd: [] };
459+
googletag.cmd.push(function () {
460+
for (const [key, values] of Object.entries(tdata)) {
461+
googletag.pubads().setTargeting(key, values);
462+
}
463+
googletag.pubads().refresh();
464+
});
465+
};
466+
```
467+
468+
Because `ctxTargetingKeyValues()` reads the cached response, the instance should be initialized with `initContextual: true` so the segments are fetched during initialization and the cache is likely populated by the time `loadGAM()` runs:
469+
470+
```javascript
471+
loadGAM(optable.instance.ctxTargetingKeyValues());
472+
```
473+
474+
If you are not using `initContextual`, fetch the segments explicitly and call `loadGAM()` once `ctxSegments()` resolves (falling back to an untargeted load on error):
475+
476+
```javascript
477+
optable.cmd.push(function () {
478+
optable.instance
479+
.ctxSegments()
480+
.then(loadGAM)
481+
.catch((err) => {
482+
loadGAM();
483+
});
484+
});
485+
```
486+
487+
You can also rename and allow-list the GAM keys by passing a `taxonomyKeys` map to `ctxTargetingKeyValues()` (only the taxonomies present in the map are emitted):
488+
489+
```javascript
490+
// Emit only the "iab_ct_3_1" taxonomy, under the GAM key "ctx_iab":
491+
loadGAM(optable.instance.ctxTargetingKeyValues({ iab_ct_3_1: "ctx_iab" }));
492+
```
493+
407494
## Using a script tag
408495

409496
For each [SDK release](https://github.com/Optable/optable-web-sdk/releases), a webpack-generated browser bundle targeting the browsers list described by `pnpm dlx browserslist "> 0.25%, not dead"` can be loaded on a website via a `script` tag.

demos/index-nocookies.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ <h5>Audience Targeting</h5>
131131
>.
132132
</td>
133133
</tr>
134+
<tr>
135+
<td><a href="/vanilla/nocookies/targeting/ctx_segments.html">contextual segments</a></td>
136+
<td>
137+
Shows how to call <code>ctxSegments()</code> to classify a page URL against contextual taxonomies
138+
(e.g. the <a href="https://iabtechlab.com/standards/content-taxonomy/">IAB Content Taxonomy</a>) and
139+
inspect the categories and confidence scores the DCN returns.
140+
</td>
141+
</tr>
134142
</tbody>
135143
</table>
136144
</div>

demos/index.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,14 @@ <h5>Audience Targeting</h5>
138138
>.
139139
</td>
140140
</tr>
141+
<tr>
142+
<td><a href="/vanilla/targeting/ctx_segments.html">contextual segments</a></td>
143+
<td>
144+
Shows how to call <code>ctxSegments()</code> to classify a page URL against contextual taxonomies
145+
(e.g. the <a href="https://iabtechlab.com/standards/content-taxonomy/">IAB Content Taxonomy</a>) and
146+
inspect the categories and confidence scores the DCN returns.
147+
</td>
148+
</tr>
141149
</tbody>
142150
</table>
143151
</div>

0 commit comments

Comments
 (0)