@skhema/embed 0.1.0 → 0.1.2

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.es.js CHANGED
@@ -263,10 +263,10 @@ class AnalyticsBatcher {
263
263
  }
264
264
  async sendComponentEmbeds(embeds) {
265
265
  if (embeds.length === 0) return;
266
- try {
267
- const payload = {
268
- action: "component_embed",
269
- events: embeds.map((embed) => ({
266
+ for (const embed of embeds) {
267
+ try {
268
+ const payload = {
269
+ action: "component_embed",
270
270
  contributor_id: embed.contributorId,
271
271
  component_type: embed.componentType,
272
272
  component_hash: embed.componentHash,
@@ -279,11 +279,11 @@ class AnalyticsBatcher {
279
279
  page_url: embed.pageUrl,
280
280
  page_title: embed.pageTitle || "",
281
281
  user_agent: embed.userAgent || ""
282
- }))
283
- };
284
- await sendWithRetry(ANALYTICS_ENDPOINT, payload, "json");
285
- } catch (error) {
286
- console.debug("Component embed tracking failed:", error);
282
+ };
283
+ await sendWithRetry(ANALYTICS_ENDPOINT, payload, "json");
284
+ } catch (error) {
285
+ console.debug("Component embed tracking failed:", error);
286
+ }
287
287
  }
288
288
  }
289
289
  async sendComponentClicks(clicks) {
@@ -310,11 +310,90 @@ class AnalyticsBatcher {
310
310
  }
311
311
  }
312
312
  }
313
- // Ensure batch is sent when page unloads
313
+ // Ensure batch is sent when page unloads.
314
+ // Uses sendBeacon for reliability — async fetch won't complete during unload.
314
315
  flush() {
315
- const hasItems = this.batch.embeds.length > 0 || this.batch.clicks.length > 0 || this.batch.componentEmbeds.length > 0 || this.batch.componentClicks.length > 0;
316
- if (hasItems) {
317
- this.sendBatch();
316
+ if (this.batchTimeout) {
317
+ clearTimeout(this.batchTimeout);
318
+ this.batchTimeout = null;
319
+ }
320
+ const currentBatch = { ...this.batch };
321
+ this.batch = {
322
+ embeds: [],
323
+ clicks: [],
324
+ componentEmbeds: [],
325
+ componentClicks: []
326
+ };
327
+ if (currentBatch.embeds.length > 0) {
328
+ const payload = {
329
+ action: "embed",
330
+ events: currentBatch.embeds.map((embed) => ({
331
+ contributor_id: embed.contributorId,
332
+ element_type: embed.elementType,
333
+ content_hash: embed.contentHash,
334
+ content: embed.content,
335
+ page_url: embed.pageUrl,
336
+ page_title: embed.pageTitle || "",
337
+ user_agent: embed.userAgent || ""
338
+ }))
339
+ };
340
+ this.sendViaBeacon(payload);
341
+ }
342
+ for (const click of currentBatch.clicks) {
343
+ this.sendViaBeacon({
344
+ action: "click",
345
+ contributor_id: click.contributor_id,
346
+ content_hash: click.content_hash,
347
+ source_url: click.source_url,
348
+ element_type: click.element_type
349
+ });
350
+ }
351
+ for (const embed of currentBatch.componentEmbeds) {
352
+ this.sendViaBeacon({
353
+ action: "component_embed",
354
+ contributor_id: embed.contributorId,
355
+ component_type: embed.componentType,
356
+ component_hash: embed.componentHash,
357
+ title: embed.title,
358
+ elements: embed.elements.map((el) => ({
359
+ element_type: el.elementType,
360
+ content: el.content,
361
+ content_hash: el.contentHash
362
+ })),
363
+ page_url: embed.pageUrl,
364
+ page_title: embed.pageTitle || "",
365
+ user_agent: embed.userAgent || ""
366
+ });
367
+ }
368
+ for (const click of currentBatch.componentClicks) {
369
+ this.sendViaBeacon({
370
+ action: "component_click",
371
+ contributor_id: click.contributor_id,
372
+ component_type: click.component_type,
373
+ component_hash: click.component_hash,
374
+ title: click.title,
375
+ source_url: click.source_url
376
+ });
377
+ }
378
+ }
379
+ sendViaBeacon(payload) {
380
+ try {
381
+ if (typeof navigator !== "undefined" && navigator.sendBeacon) {
382
+ navigator.sendBeacon(
383
+ ANALYTICS_ENDPOINT,
384
+ new Blob([JSON.stringify(payload)], { type: "application/json" })
385
+ );
386
+ } else {
387
+ fetch(ANALYTICS_ENDPOINT, {
388
+ method: "POST",
389
+ headers: { "Content-Type": "application/json" },
390
+ body: JSON.stringify(payload),
391
+ credentials: "omit",
392
+ keepalive: true
393
+ }).catch(() => {
394
+ });
395
+ }
396
+ } catch {
318
397
  }
319
398
  }
320
399
  }
@@ -964,7 +1043,13 @@ class SkhemaComponent extends HTMLElement {
964
1043
  this.addEventListener("skhema:element-ready", () => {
965
1044
  this.scheduleRender();
966
1045
  });
967
- this.scheduleRender();
1046
+ if (typeof customElements.whenDefined === "function") {
1047
+ customElements.whenDefined("skhema-element").then(() => {
1048
+ this.scheduleRender();
1049
+ });
1050
+ } else {
1051
+ this.scheduleRender();
1052
+ }
968
1053
  this.setupThemeListeners();
969
1054
  } catch (error) {
970
1055
  this.renderError("Failed to initialize component", error);