@skhema/embed 0.1.1 → 0.1.3

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
  }
@@ -950,6 +1029,7 @@ class SkhemaComponent extends HTMLElement {
950
1029
  return [
951
1030
  "component-type",
952
1031
  "contributor-id",
1032
+ "contributor-name",
953
1033
  "title",
954
1034
  "theme",
955
1035
  "track-analytics",
@@ -964,7 +1044,13 @@ class SkhemaComponent extends HTMLElement {
964
1044
  this.addEventListener("skhema:element-ready", () => {
965
1045
  this.scheduleRender();
966
1046
  });
967
- this.scheduleRender();
1047
+ if (typeof customElements.whenDefined === "function") {
1048
+ customElements.whenDefined("skhema-element").then(() => {
1049
+ this.scheduleRender();
1050
+ });
1051
+ } else {
1052
+ this.scheduleRender();
1053
+ }
968
1054
  this.setupThemeListeners();
969
1055
  } catch (error) {
970
1056
  this.renderError("Failed to initialize component", error);
@@ -1083,7 +1169,8 @@ class SkhemaComponent extends HTMLElement {
1083
1169
  const componentLabel = getComponentTypeLabel(component_type);
1084
1170
  const acronym = getComponentTypeAcronym(component_type);
1085
1171
  const colors = COMPONENT_COLORS[component_type];
1086
- const displayName = this.formatContributorName(contributor_id);
1172
+ const contributorName = this.getAttribute("contributor-name");
1173
+ const displayName = contributorName || this.formatContributorName(contributor_id);
1087
1174
  const themeAttribute = this.getAttribute("theme") || "auto";
1088
1175
  const actualTheme = this.getActualTheme(themeAttribute);
1089
1176
  const redirectUrl = generateComponentRedirectUrl(
@@ -1641,6 +1728,7 @@ class SkhemaElement extends HTMLElement {
1641
1728
  return [
1642
1729
  "element-type",
1643
1730
  "contributor-id",
1731
+ "contributor-name",
1644
1732
  "content",
1645
1733
  "source-url",
1646
1734
  "theme",
@@ -1761,7 +1849,8 @@ class SkhemaElement extends HTMLElement {
1761
1849
  );
1762
1850
  const themeAttribute = this.getAttribute("theme") || "auto";
1763
1851
  const actualTheme = this.getActualTheme(themeAttribute);
1764
- const displayName = this.formatContributorName(contributor_id);
1852
+ const contributorName = this.getAttribute("contributor-name");
1853
+ const displayName = contributorName || this.formatContributorName(contributor_id);
1765
1854
  const componentType = resolveComponentType(element_type);
1766
1855
  const acronym = getComponentTypeAcronym(componentType);
1767
1856
  const colors = COMPONENT_COLORS[componentType];