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