@omega.js/client 0.1.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/LICENSE +98 -0
- package/README.md +874 -0
- package/dist/index.js +999 -0
- package/dist/modules/analytics.js +584 -0
- package/dist/modules/auth.js +469 -0
- package/dist/modules/bindings.js +319 -0
- package/dist/modules/device.js +282 -0
- package/dist/modules/dom.js +96 -0
- package/dist/modules/features.js +30 -0
- package/dist/modules/firestore.js +313 -0
- package/dist/modules/form-manager.js +1577 -0
- package/dist/modules/icon-core.js +226 -0
- package/dist/modules/icon-renderer.js +149 -0
- package/dist/modules/live-page.js +235 -0
- package/dist/modules/logger.js +36 -0
- package/dist/modules/motion.js +853 -0
- package/dist/modules/notifications.js +433 -0
- package/dist/modules/path-prefix.js +22 -0
- package/dist/modules/request.js +223 -0
- package/dist/modules/sentry.js +108 -0
- package/dist/modules/service-worker.js +237 -0
- package/dist/modules/storage.js +133 -0
- package/dist/modules/triggers.js +117 -0
- package/dist/modules/utilities.js +479 -0
- package/dist/modules/vert-document.js +354 -0
- package/dist/modules/verts.js +1133 -0
- package/dist/vendor/account/engine.js +182 -0
- package/dist/vendor/account/features.js +220 -0
- package/dist/vendor/account/index.js +53 -0
- package/dist/vendor/account/schema.js +272 -0
- package/dist/vendor/account/subscription.js +38 -0
- package/dist/vendor/analytics/adapters/ga4.js +26 -0
- package/dist/vendor/analytics/adapters/meta.js +26 -0
- package/dist/vendor/analytics/adapters/resolve.js +130 -0
- package/dist/vendor/analytics/adapters/tiktok.js +27 -0
- package/dist/vendor/analytics/catalog.js +908 -0
- package/dist/vendor/analytics/consent.js +49 -0
- package/dist/vendor/analytics/core.js +141 -0
- package/dist/vendor/analytics/identity.js +136 -0
- package/dist/vendor/analytics/index.js +170 -0
- package/dist/vendor/analytics/logger.js +40 -0
- package/dist/vendor/analytics/transports/browser.js +110 -0
- package/dist/vendor/monitoring/browser.js +207 -0
- package/dist/vendor/monitoring/core.js +180 -0
- package/dist/vendor/monitoring/logger.js +39 -0
- package/docs/architecture.md +59 -0
- package/docs/bindings.md +235 -0
- package/docs/build-system.md +32 -0
- package/docs/cdp-debugging.md +29 -0
- package/docs/code-patterns.md +96 -0
- package/docs/common-tasks.md +36 -0
- package/docs/dependencies.md +19 -0
- package/docs/index.md +159 -0
- package/docs/modules.md +180 -0
- package/docs/shared/agent-docs.md +89 -0
- package/docs/shared/analytics.md +612 -0
- package/docs/shared/brands.md +51 -0
- package/docs/shared/breaking-changes.md +497 -0
- package/docs/shared/config.md +1387 -0
- package/docs/shared/deploys.md +215 -0
- package/docs/shared/icons.md +201 -0
- package/docs/shared/local-dev.md +147 -0
- package/docs/shared/logging.md +202 -0
- package/docs/shared/monitoring.md +153 -0
- package/docs/shared/publishing.md +183 -0
- package/docs/shared/rulings.md +34 -0
- package/docs/shared/testing.md +147 -0
- package/docs/shared/theming.md +604 -0
- package/docs/shared/translation.md +291 -0
- package/docs/shared/updates.md +61 -0
- package/docs/testing.md +9 -0
- package/package.json +65 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vert unit document — the ONE producer of vert unit documents.
|
|
3
|
+
*
|
|
4
|
+
* Every vert unit document in the stack comes from this function: the
|
|
5
|
+
* backend's /omega/verts/serve route calls it for house inventory, and the
|
|
6
|
+
* client's terminal promo lane calls it for the built-in omegajs.dev unit.
|
|
7
|
+
* There is no second template anywhere.
|
|
8
|
+
*
|
|
9
|
+
* The document is self-contained (inline css/js, no external request), it
|
|
10
|
+
* reports its rendered size to the host ('omega-vert:set-dimensions' on load
|
|
11
|
+
* plus a ResizeObserver), it forwards clicks ('omega-vert:click'), and it
|
|
12
|
+
* NEVER refreshes itself — the host owns the lifecycle (docs/web/ads-system.md).
|
|
13
|
+
*
|
|
14
|
+
* The look is one media row: thumbnail left, title with a clamped muted
|
|
15
|
+
* description beside it, a hairline, then a muted label on the left and the
|
|
16
|
+
* accent CTA button on the right. The document is content-sized: its natural
|
|
17
|
+
* height IS the card height, and the host shrinks the frame to the reported
|
|
18
|
+
* height (the slot preset is a ceiling, never a floor).
|
|
19
|
+
*
|
|
20
|
+
* Escaping posture: every text field is escaped. `imageMarkup` is the ONE
|
|
21
|
+
* trusted-markup slot and takes framework constants only (the promo's inline
|
|
22
|
+
* svg mark) — never anything from vert data.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// Fixed postMessage vocabulary — the host and the document agree here
|
|
26
|
+
export const MESSAGE_DIMENSIONS = 'omega-vert:set-dimensions';
|
|
27
|
+
export const MESSAGE_CLICK = 'omega-vert:click';
|
|
28
|
+
|
|
29
|
+
// The one utm_medium every vert click carries, both lanes
|
|
30
|
+
export const UTM_MEDIUM = 'omega-vert';
|
|
31
|
+
|
|
32
|
+
// The promo lane's campaign (a served vert's campaign is its own id)
|
|
33
|
+
export const UTM_CAMPAIGN_PROMO = 'omega-promo';
|
|
34
|
+
|
|
35
|
+
// The default accent pair: neutral ink, the served-unit look
|
|
36
|
+
export const DEFAULT_ACCENT = {
|
|
37
|
+
light: '#1a1a1a',
|
|
38
|
+
lightText: '#ffffff',
|
|
39
|
+
dark: '#eeeeee',
|
|
40
|
+
darkText: '#1a1a1a',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// The omega indigo pair — the promo's accent
|
|
44
|
+
export const OMEGA_ACCENT = {
|
|
45
|
+
light: '#4f46e5',
|
|
46
|
+
lightText: '#ffffff',
|
|
47
|
+
dark: '#8b85f5',
|
|
48
|
+
darkText: '#16181d',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// A slot shorter than this (or narrower than the width below) tightens the
|
|
52
|
+
// row: smaller thumbnail, no description line
|
|
53
|
+
const COMPACT_HEIGHT_UNDER = 120;
|
|
54
|
+
const COMPACT_WIDTH_UNDER = 360;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Escape a string for safe insertion into HTML text/attribute context.
|
|
58
|
+
* @param {*} input - any value
|
|
59
|
+
* @returns {string} escaped text
|
|
60
|
+
*/
|
|
61
|
+
export function escapeHtml(input) {
|
|
62
|
+
return `${input === undefined || input === null ? '' : input}`
|
|
63
|
+
.replace(/&/g, '&')
|
|
64
|
+
.replace(/</g, '<')
|
|
65
|
+
.replace(/>/g, '>')
|
|
66
|
+
.replace(/"/g, '"');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Tag a click destination with the vert UTM set — the ONE place either lane
|
|
71
|
+
* builds it (the promo's omegajs.dev link here in the client, a served vert's
|
|
72
|
+
* stored link in the backend's redirect route, which imports this function).
|
|
73
|
+
*
|
|
74
|
+
* Existing params WIN: a key already on the URL is never overwritten, so an
|
|
75
|
+
* advertiser's own tagging survives verbatim. A non-http(s) or unparseable
|
|
76
|
+
* value comes back untouched.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} url - the destination URL
|
|
79
|
+
* @param {object} [params]
|
|
80
|
+
* @param {string} [params.source] - utm_source (the host page's hostname)
|
|
81
|
+
* @param {string} [params.medium] - utm_medium (UTM_MEDIUM)
|
|
82
|
+
* @param {string} [params.campaign] - utm_campaign (vert id / UTM_CAMPAIGN_PROMO)
|
|
83
|
+
* @param {string} [params.content] - utm_content (the slot's size preset)
|
|
84
|
+
* @returns {string} the tagged URL
|
|
85
|
+
*/
|
|
86
|
+
export function applyVertUtm(url, params) {
|
|
87
|
+
params = params || {};
|
|
88
|
+
|
|
89
|
+
let target;
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
target = new URL(url);
|
|
93
|
+
} catch (e) {
|
|
94
|
+
return `${url === undefined || url === null ? '' : url}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (target.protocol !== 'http:' && target.protocol !== 'https:') {
|
|
98
|
+
return url;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const utm = {
|
|
102
|
+
utm_source: params.source,
|
|
103
|
+
utm_medium: params.medium,
|
|
104
|
+
utm_campaign: params.campaign,
|
|
105
|
+
utm_content: params.content,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
for (const [key, value] of Object.entries(utm)) {
|
|
109
|
+
// Never overwrite the advertiser's own tagging
|
|
110
|
+
if (!value || target.searchParams.has(key)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
target.searchParams.set(key, value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return target.toString();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Parse a dimension hint to a positive integer, 0 when unusable.
|
|
122
|
+
* @param {*} value
|
|
123
|
+
* @returns {number}
|
|
124
|
+
*/
|
|
125
|
+
function toPx(value) {
|
|
126
|
+
const num = parseInt(value, 10);
|
|
127
|
+
return Number.isFinite(num) && num > 0 ? num : 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Render the vert unit document.
|
|
132
|
+
* @param {object} options
|
|
133
|
+
* @param {string} [options.id] - unit id echoed in every postMessage
|
|
134
|
+
* @param {string} [options.href] - the click destination (redirect route / promo url)
|
|
135
|
+
* @param {string} [options.title] - headline text
|
|
136
|
+
* @param {string} [options.description] - one-line pitch under the headline
|
|
137
|
+
* @param {string} [options.button] - CTA button label
|
|
138
|
+
* @param {string} [options.label] - muted bottom-left label (default 'Sponsored')
|
|
139
|
+
* @param {string} [options.imageUrl] - http(s) thumbnail source (escaped)
|
|
140
|
+
* @param {string} [options.imageMarkup] - TRUSTED thumbnail markup (framework constants only)
|
|
141
|
+
* @param {string} [options.theme] - 'light' | 'dark'; anything else follows the OS
|
|
142
|
+
* @param {number|string} [options.width] - slot width hint in px
|
|
143
|
+
* @param {number|string} [options.height] - slot height hint in px
|
|
144
|
+
* @param {object} [options.accent] - { light, lightText, dark, darkText } accent pair
|
|
145
|
+
* @param {string} [options.targetOrigin] - origin every postMessage targets ('*' when unknown)
|
|
146
|
+
* @returns {string} a complete html document
|
|
147
|
+
*/
|
|
148
|
+
// The generated script reports the CARD's box as the height, never the root
|
|
149
|
+
// element's: the frame arrives at the slot preset, so documentElement
|
|
150
|
+
// .scrollHeight can never measure shorter than the frame and the host would
|
|
151
|
+
// never shrink. (Kept out of the template literal — srcdoc ships verbatim.)
|
|
152
|
+
export function renderVertDocument(options) {
|
|
153
|
+
options = options || {};
|
|
154
|
+
|
|
155
|
+
const theme = options.theme === 'dark' || options.theme === 'light'
|
|
156
|
+
? options.theme
|
|
157
|
+
: '';
|
|
158
|
+
const width = toPx(options.width);
|
|
159
|
+
const height = toPx(options.height);
|
|
160
|
+
const accent = options.accent || DEFAULT_ACCENT;
|
|
161
|
+
const targetOrigin = options.targetOrigin || '*';
|
|
162
|
+
const id = options.id || '';
|
|
163
|
+
|
|
164
|
+
// A short or narrow slot tightens the row instead of wrapping tall; a
|
|
165
|
+
// skyscraper-shaped slot stacks the thumbnail above the copy. The compact
|
|
166
|
+
// numbers below (32px thumb, 6px paddings and rule margins) are budgeted so
|
|
167
|
+
// the whole card lands under the leaderboard ceiling — the shortest preset,
|
|
168
|
+
// and the one a taller card would get cropped by.
|
|
169
|
+
const compact = (height > 0 && height < COMPACT_HEIGHT_UNDER) || (width > 0 && width < COMPACT_WIDTH_UNDER);
|
|
170
|
+
const stacked = width > 0 && height >= width * 2;
|
|
171
|
+
const thumbPx = compact ? 32 : 56;
|
|
172
|
+
|
|
173
|
+
const thumb = options.imageMarkup
|
|
174
|
+
? `<span class="omega-vert-thumb">${options.imageMarkup}</span>`
|
|
175
|
+
: options.imageUrl
|
|
176
|
+
? `<span class="omega-vert-thumb"><img class="omega-vert-image" src="${escapeHtml(options.imageUrl)}" alt=""></span>`
|
|
177
|
+
: '';
|
|
178
|
+
const description = options.description && !compact
|
|
179
|
+
? `<span class="omega-vert-description">${escapeHtml(options.description)}</span>`
|
|
180
|
+
: '';
|
|
181
|
+
const button = options.button
|
|
182
|
+
? `<span class="omega-vert-button">${escapeHtml(options.button)}</span>`
|
|
183
|
+
: '';
|
|
184
|
+
|
|
185
|
+
return `<!DOCTYPE html>
|
|
186
|
+
<html lang="en"${theme ? ` data-theme="${theme}"` : ''}>
|
|
187
|
+
<head>
|
|
188
|
+
<meta charset="utf-8">
|
|
189
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
190
|
+
<meta name="robots" content="noindex, nofollow">
|
|
191
|
+
<title>Sponsored</title>
|
|
192
|
+
<style>
|
|
193
|
+
:root {
|
|
194
|
+
color-scheme: light dark;
|
|
195
|
+
--omega-vert-bg: #ffffff;
|
|
196
|
+
--omega-vert-border: #e2e2e2;
|
|
197
|
+
--omega-vert-text: #16181d;
|
|
198
|
+
--omega-vert-muted: #5a6270;
|
|
199
|
+
--omega-vert-accent: ${accent.light};
|
|
200
|
+
--omega-vert-accent-text: ${accent.lightText};
|
|
201
|
+
}
|
|
202
|
+
@media (prefers-color-scheme: dark) {
|
|
203
|
+
:root {
|
|
204
|
+
--omega-vert-bg: #16181d;
|
|
205
|
+
--omega-vert-border: #333333;
|
|
206
|
+
--omega-vert-text: #f2f4f8;
|
|
207
|
+
--omega-vert-muted: #9aa3b2;
|
|
208
|
+
--omega-vert-accent: ${accent.dark};
|
|
209
|
+
--omega-vert-accent-text: ${accent.darkText};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
:root[data-theme="light"] {
|
|
213
|
+
color-scheme: light;
|
|
214
|
+
--omega-vert-bg: #ffffff;
|
|
215
|
+
--omega-vert-border: #e2e2e2;
|
|
216
|
+
--omega-vert-text: #16181d;
|
|
217
|
+
--omega-vert-muted: #5a6270;
|
|
218
|
+
--omega-vert-accent: ${accent.light};
|
|
219
|
+
--omega-vert-accent-text: ${accent.lightText};
|
|
220
|
+
}
|
|
221
|
+
:root[data-theme="dark"] {
|
|
222
|
+
color-scheme: dark;
|
|
223
|
+
--omega-vert-bg: #16181d;
|
|
224
|
+
--omega-vert-border: #333333;
|
|
225
|
+
--omega-vert-text: #f2f4f8;
|
|
226
|
+
--omega-vert-muted: #9aa3b2;
|
|
227
|
+
--omega-vert-accent: ${accent.dark};
|
|
228
|
+
--omega-vert-accent-text: ${accent.darkText};
|
|
229
|
+
}
|
|
230
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
231
|
+
/* The root stays transparent so ONLY the card paints. color-scheme above
|
|
232
|
+
decides the canvas the browser falls back to whenever it rasterizes the
|
|
233
|
+
frame without that transparency (first paint, a re-raster on scroll or
|
|
234
|
+
zoom): light-mode white behind a dark card is the corner-notch bug. */
|
|
235
|
+
html, body { background: transparent; }
|
|
236
|
+
body { font-family: -apple-system, system-ui, sans-serif; }
|
|
237
|
+
.omega-vert {
|
|
238
|
+
display: block;
|
|
239
|
+
background: var(--omega-vert-bg);
|
|
240
|
+
border: 1px solid var(--omega-vert-border);
|
|
241
|
+
border-radius: 10px;
|
|
242
|
+
color: var(--omega-vert-text);
|
|
243
|
+
overflow: hidden;
|
|
244
|
+
text-decoration: none;
|
|
245
|
+
padding: ${compact ? '6px 10px' : '10px 12px'};
|
|
246
|
+
${width ? `max-width: ${width}px;` : ''}
|
|
247
|
+
}
|
|
248
|
+
.omega-vert-row {
|
|
249
|
+
display: flex;
|
|
250
|
+
align-items: center;
|
|
251
|
+
gap: ${compact ? '8px' : '10px'};
|
|
252
|
+
${stacked ? 'flex-direction: column; align-items: flex-start;' : ''}
|
|
253
|
+
}
|
|
254
|
+
.omega-vert-thumb {
|
|
255
|
+
display: block;
|
|
256
|
+
flex: 0 0 auto;
|
|
257
|
+
width: ${stacked ? '100%' : `${thumbPx}px`};
|
|
258
|
+
height: ${thumbPx}px;
|
|
259
|
+
border-radius: 8px;
|
|
260
|
+
overflow: hidden;
|
|
261
|
+
line-height: 0;
|
|
262
|
+
}
|
|
263
|
+
.omega-vert-image { display: block; width: 100%; height: 100%; object-fit: cover; }
|
|
264
|
+
.omega-vert-copy { display: block; min-width: 0; }
|
|
265
|
+
.omega-vert-title {
|
|
266
|
+
display: block;
|
|
267
|
+
font-size: ${compact ? '13px' : '14px'};
|
|
268
|
+
font-weight: 600;
|
|
269
|
+
line-height: 1.3;
|
|
270
|
+
overflow: hidden;
|
|
271
|
+
text-overflow: ellipsis;
|
|
272
|
+
}
|
|
273
|
+
.omega-vert-description {
|
|
274
|
+
display: -webkit-box;
|
|
275
|
+
-webkit-line-clamp: ${stacked ? 2 : 1};
|
|
276
|
+
-webkit-box-orient: vertical;
|
|
277
|
+
overflow: hidden;
|
|
278
|
+
margin-top: 2px;
|
|
279
|
+
font-size: 12px;
|
|
280
|
+
line-height: 1.35;
|
|
281
|
+
color: var(--omega-vert-muted);
|
|
282
|
+
}
|
|
283
|
+
.omega-vert-rule {
|
|
284
|
+
display: block;
|
|
285
|
+
height: 1px;
|
|
286
|
+
margin: ${compact ? '6px 0' : '10px 0'};
|
|
287
|
+
background: var(--omega-vert-border);
|
|
288
|
+
}
|
|
289
|
+
.omega-vert-foot {
|
|
290
|
+
display: flex;
|
|
291
|
+
align-items: center;
|
|
292
|
+
justify-content: space-between;
|
|
293
|
+
gap: 10px;
|
|
294
|
+
}
|
|
295
|
+
.omega-vert-label { font-size: 11px; color: var(--omega-vert-muted); }
|
|
296
|
+
.omega-vert-button {
|
|
297
|
+
display: inline-block;
|
|
298
|
+
flex: 0 0 auto;
|
|
299
|
+
background: var(--omega-vert-accent);
|
|
300
|
+
color: var(--omega-vert-accent-text);
|
|
301
|
+
border-radius: 6px;
|
|
302
|
+
font-size: 12px;
|
|
303
|
+
font-weight: 600;
|
|
304
|
+
line-height: 1.2;
|
|
305
|
+
padding: 6px 10px;
|
|
306
|
+
white-space: nowrap;
|
|
307
|
+
}
|
|
308
|
+
</style>
|
|
309
|
+
</head>
|
|
310
|
+
<body>
|
|
311
|
+
<a class="omega-vert" id="omega-vert" href="${escapeHtml(options.href)}" target="_blank" rel="noopener noreferrer sponsored">
|
|
312
|
+
<span class="omega-vert-row">
|
|
313
|
+
${thumb}
|
|
314
|
+
<span class="omega-vert-copy">
|
|
315
|
+
<span class="omega-vert-title">${escapeHtml(options.title)}</span>
|
|
316
|
+
${description}
|
|
317
|
+
</span>
|
|
318
|
+
</span>
|
|
319
|
+
<span class="omega-vert-rule"></span>
|
|
320
|
+
<span class="omega-vert-foot">
|
|
321
|
+
<span class="omega-vert-label">${escapeHtml(options.label || 'Sponsored')}</span>
|
|
322
|
+
${button}
|
|
323
|
+
</span>
|
|
324
|
+
</a>
|
|
325
|
+
<script>
|
|
326
|
+
(function () {
|
|
327
|
+
var TARGET_ORIGIN = ${JSON.stringify(targetOrigin)};
|
|
328
|
+
var VERT_ID = ${JSON.stringify(id)};
|
|
329
|
+
var card = document.getElementById('omega-vert');
|
|
330
|
+
|
|
331
|
+
function post(message) {
|
|
332
|
+
if (!window.parent || window.parent === window) return;
|
|
333
|
+
window.parent.postMessage(message, TARGET_ORIGIN);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function reportDimensions() {
|
|
337
|
+
var doc = document.documentElement;
|
|
338
|
+
post({ type: ${JSON.stringify(MESSAGE_DIMENSIONS)}, id: VERT_ID, width: doc.scrollWidth, height: Math.ceil(card.getBoundingClientRect().height) });
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
window.addEventListener('load', reportDimensions);
|
|
342
|
+
|
|
343
|
+
if (window.ResizeObserver) {
|
|
344
|
+
new ResizeObserver(reportDimensions).observe(card);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
card.addEventListener('click', function () {
|
|
348
|
+
post({ type: ${JSON.stringify(MESSAGE_CLICK)}, id: VERT_ID });
|
|
349
|
+
});
|
|
350
|
+
})();
|
|
351
|
+
</script>
|
|
352
|
+
</body>
|
|
353
|
+
</html>`;
|
|
354
|
+
}
|