@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.
Files changed (72) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +874 -0
  3. package/dist/index.js +999 -0
  4. package/dist/modules/analytics.js +584 -0
  5. package/dist/modules/auth.js +469 -0
  6. package/dist/modules/bindings.js +319 -0
  7. package/dist/modules/device.js +282 -0
  8. package/dist/modules/dom.js +96 -0
  9. package/dist/modules/features.js +30 -0
  10. package/dist/modules/firestore.js +313 -0
  11. package/dist/modules/form-manager.js +1577 -0
  12. package/dist/modules/icon-core.js +226 -0
  13. package/dist/modules/icon-renderer.js +149 -0
  14. package/dist/modules/live-page.js +235 -0
  15. package/dist/modules/logger.js +36 -0
  16. package/dist/modules/motion.js +853 -0
  17. package/dist/modules/notifications.js +433 -0
  18. package/dist/modules/path-prefix.js +22 -0
  19. package/dist/modules/request.js +223 -0
  20. package/dist/modules/sentry.js +108 -0
  21. package/dist/modules/service-worker.js +237 -0
  22. package/dist/modules/storage.js +133 -0
  23. package/dist/modules/triggers.js +117 -0
  24. package/dist/modules/utilities.js +479 -0
  25. package/dist/modules/vert-document.js +354 -0
  26. package/dist/modules/verts.js +1133 -0
  27. package/dist/vendor/account/engine.js +182 -0
  28. package/dist/vendor/account/features.js +220 -0
  29. package/dist/vendor/account/index.js +53 -0
  30. package/dist/vendor/account/schema.js +272 -0
  31. package/dist/vendor/account/subscription.js +38 -0
  32. package/dist/vendor/analytics/adapters/ga4.js +26 -0
  33. package/dist/vendor/analytics/adapters/meta.js +26 -0
  34. package/dist/vendor/analytics/adapters/resolve.js +130 -0
  35. package/dist/vendor/analytics/adapters/tiktok.js +27 -0
  36. package/dist/vendor/analytics/catalog.js +908 -0
  37. package/dist/vendor/analytics/consent.js +49 -0
  38. package/dist/vendor/analytics/core.js +141 -0
  39. package/dist/vendor/analytics/identity.js +136 -0
  40. package/dist/vendor/analytics/index.js +170 -0
  41. package/dist/vendor/analytics/logger.js +40 -0
  42. package/dist/vendor/analytics/transports/browser.js +110 -0
  43. package/dist/vendor/monitoring/browser.js +207 -0
  44. package/dist/vendor/monitoring/core.js +180 -0
  45. package/dist/vendor/monitoring/logger.js +39 -0
  46. package/docs/architecture.md +59 -0
  47. package/docs/bindings.md +235 -0
  48. package/docs/build-system.md +32 -0
  49. package/docs/cdp-debugging.md +29 -0
  50. package/docs/code-patterns.md +96 -0
  51. package/docs/common-tasks.md +36 -0
  52. package/docs/dependencies.md +19 -0
  53. package/docs/index.md +159 -0
  54. package/docs/modules.md +180 -0
  55. package/docs/shared/agent-docs.md +89 -0
  56. package/docs/shared/analytics.md +612 -0
  57. package/docs/shared/brands.md +51 -0
  58. package/docs/shared/breaking-changes.md +497 -0
  59. package/docs/shared/config.md +1387 -0
  60. package/docs/shared/deploys.md +215 -0
  61. package/docs/shared/icons.md +201 -0
  62. package/docs/shared/local-dev.md +147 -0
  63. package/docs/shared/logging.md +202 -0
  64. package/docs/shared/monitoring.md +153 -0
  65. package/docs/shared/publishing.md +183 -0
  66. package/docs/shared/rulings.md +34 -0
  67. package/docs/shared/testing.md +147 -0
  68. package/docs/shared/theming.md +604 -0
  69. package/docs/shared/translation.md +291 -0
  70. package/docs/shared/updates.md +61 -0
  71. package/docs/testing.md +9 -0
  72. 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, '&lt;')
65
+ .replace(/>/g, '&gt;')
66
+ .replace(/"/g, '&quot;');
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
+ }