@rmdes/indiekit-endpoint-microsub 1.0.6 → 1.0.7

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.
@@ -303,6 +303,18 @@ export async function item(request, response) {
303
303
  });
304
304
  }
305
305
 
306
+ /**
307
+ * Ensure value is a string URL
308
+ * @param {string|object|undefined} value - Value to check
309
+ * @returns {string|undefined} String value or undefined
310
+ */
311
+ function ensureString(value) {
312
+ if (!value) return;
313
+ if (typeof value === "string") return value;
314
+ if (typeof value === "object" && value.url) return value.url;
315
+ return String(value);
316
+ }
317
+
306
318
  /**
307
319
  * Compose response form
308
320
  * @param {object} request - Express request
@@ -324,10 +336,10 @@ export async function compose(request, response) {
324
336
 
325
337
  response.render("compose", {
326
338
  title: request.__("microsub.compose.title"),
327
- replyTo: replyTo || reply,
328
- likeOf: likeOf || like,
329
- repostOf: repostOf || repost,
330
- bookmarkOf: bookmarkOf || bookmark,
339
+ replyTo: ensureString(replyTo || reply),
340
+ likeOf: ensureString(likeOf || like),
341
+ repostOf: ensureString(repostOf || repost),
342
+ bookmarkOf: ensureString(bookmarkOf || bookmark),
331
343
  baseUrl: request.baseUrl,
332
344
  });
333
345
  }
@@ -536,18 +536,18 @@ export function normalizeHfeedItem(entry, feedUrl) {
536
536
  );
537
537
  }
538
538
 
539
- // Interaction types
539
+ // Interaction types - normalize to string URLs
540
540
  if (properties["like-of"]) {
541
- normalized["like-of"] = properties["like-of"];
541
+ normalized["like-of"] = normalizeUrlArray(properties["like-of"]);
542
542
  }
543
543
  if (properties["repost-of"]) {
544
- normalized["repost-of"] = properties["repost-of"];
544
+ normalized["repost-of"] = normalizeUrlArray(properties["repost-of"]);
545
545
  }
546
546
  if (properties["bookmark-of"]) {
547
- normalized["bookmark-of"] = properties["bookmark-of"];
547
+ normalized["bookmark-of"] = normalizeUrlArray(properties["bookmark-of"]);
548
548
  }
549
549
  if (properties["in-reply-to"]) {
550
- normalized["in-reply-to"] = properties["in-reply-to"];
550
+ normalized["in-reply-to"] = normalizeUrlArray(properties["in-reply-to"]);
551
551
  }
552
552
 
553
553
  // RSVP
@@ -617,6 +617,36 @@ function extractPhotoUrl(photo) {
617
617
  return;
618
618
  }
619
619
 
620
+ /**
621
+ * Extract URL string from a value that may be string or object
622
+ * @param {object|string} value - URL string or object with url/value property
623
+ * @returns {string|undefined} URL string
624
+ */
625
+ function extractUrl(value) {
626
+ if (!value) {
627
+ return;
628
+ }
629
+ if (typeof value === "string") {
630
+ return value;
631
+ }
632
+ if (typeof value === "object") {
633
+ return value.value || value.url || value.href;
634
+ }
635
+ return;
636
+ }
637
+
638
+ /**
639
+ * Normalize an array of URLs that may contain strings or objects
640
+ * @param {Array} urls - Array of URL strings or objects
641
+ * @returns {Array<string>} Array of URL strings
642
+ */
643
+ function normalizeUrlArray(urls) {
644
+ if (!urls || !Array.isArray(urls)) {
645
+ return [];
646
+ }
647
+ return urls.map((u) => extractUrl(u)).filter(Boolean);
648
+ }
649
+
620
650
  /**
621
651
  * Normalize h-card author
622
652
  * @param {object|string} hcard - h-card or author name string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-microsub",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Microsub endpoint for Indiekit. Enables subscribing to feeds and reading content using the Microsub protocol.",
5
5
  "keywords": [
6
6
  "indiekit",
package/views/compose.njk CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  <h2>{{ __("microsub.compose.title") }}</h2>
10
10
 
11
- {% if replyTo %}
11
+ {% if replyTo and replyTo is string %}
12
12
  <div class="compose__context">
13
13
  {{ icon("reply") }} {{ __("microsub.compose.replyTo") }}:
14
14
  <a href="{{ replyTo }}" target="_blank" rel="noopener">
@@ -17,7 +17,7 @@
17
17
  </div>
18
18
  {% endif %}
19
19
 
20
- {% if likeOf %}
20
+ {% if likeOf and likeOf is string %}
21
21
  <div class="compose__context">
22
22
  {{ icon("like") }} {{ __("microsub.compose.likeOf") }}:
23
23
  <a href="{{ likeOf }}" target="_blank" rel="noopener">
@@ -26,7 +26,7 @@
26
26
  </div>
27
27
  {% endif %}
28
28
 
29
- {% if repostOf %}
29
+ {% if repostOf and repostOf is string %}
30
30
  <div class="compose__context">
31
31
  {{ icon("repost") }} {{ __("microsub.compose.repostOf") }}:
32
32
  <a href="{{ repostOf }}" target="_blank" rel="noopener">
@@ -35,7 +35,7 @@
35
35
  </div>
36
36
  {% endif %}
37
37
 
38
- {% if bookmarkOf %}
38
+ {% if bookmarkOf and bookmarkOf is string %}
39
39
  <div class="compose__context">
40
40
  {{ icon("bookmark") }} {{ __("microsub.compose.bookmarkOf") }}:
41
41
  <a href="{{ bookmarkOf }}" target="_blank" rel="noopener">
@@ -7,36 +7,43 @@
7
7
  data-is-read="{{ item._is_read | default(false) }}">
8
8
 
9
9
  {# Context bar for interactions (Aperture pattern) #}
10
+ {# Helper to extract URL from value that may be string or object #}
11
+ {% macro getUrl(val) %}{{ val.url or val.value or val if val is string else val }}{% endmacro %}
12
+
10
13
  {% if item["like-of"] and item["like-of"].length > 0 %}
14
+ {% set contextUrl = item['like-of'][0].url or item['like-of'][0].value or item['like-of'][0] %}
11
15
  <div class="item-card__context">
12
16
  {{ icon("like") }}
13
17
  <span>Liked</span>
14
- <a href="{{ item['like-of'][0] }}" target="_blank" rel="noopener">
15
- {{ item['like-of'][0] | replace("https://", "") | replace("http://", "") | truncate(50) }}
18
+ <a href="{{ contextUrl }}" target="_blank" rel="noopener">
19
+ {{ contextUrl | replace("https://", "") | replace("http://", "") | truncate(50) }}
16
20
  </a>
17
21
  </div>
18
22
  {% elif item["repost-of"] and item["repost-of"].length > 0 %}
23
+ {% set contextUrl = item['repost-of'][0].url or item['repost-of'][0].value or item['repost-of'][0] %}
19
24
  <div class="item-card__context">
20
25
  {{ icon("repost") }}
21
26
  <span>Reposted</span>
22
- <a href="{{ item['repost-of'][0] }}" target="_blank" rel="noopener">
23
- {{ item['repost-of'][0] | replace("https://", "") | replace("http://", "") | truncate(50) }}
27
+ <a href="{{ contextUrl }}" target="_blank" rel="noopener">
28
+ {{ contextUrl | replace("https://", "") | replace("http://", "") | truncate(50) }}
24
29
  </a>
25
30
  </div>
26
31
  {% elif item["in-reply-to"] and item["in-reply-to"].length > 0 %}
32
+ {% set contextUrl = item['in-reply-to'][0].url or item['in-reply-to'][0].value or item['in-reply-to'][0] %}
27
33
  <div class="item-card__context">
28
34
  {{ icon("reply") }}
29
35
  <span>Reply to</span>
30
- <a href="{{ item['in-reply-to'][0] }}" target="_blank" rel="noopener">
31
- {{ item['in-reply-to'][0] | replace("https://", "") | replace("http://", "") | truncate(50) }}
36
+ <a href="{{ contextUrl }}" target="_blank" rel="noopener">
37
+ {{ contextUrl | replace("https://", "") | replace("http://", "") | truncate(50) }}
32
38
  </a>
33
39
  </div>
34
40
  {% elif item["bookmark-of"] and item["bookmark-of"].length > 0 %}
41
+ {% set contextUrl = item['bookmark-of'][0].url or item['bookmark-of'][0].value or item['bookmark-of'][0] %}
35
42
  <div class="item-card__context">
36
43
  {{ icon("bookmark") }}
37
44
  <span>Bookmarked</span>
38
- <a href="{{ item['bookmark-of'][0] }}" target="_blank" rel="noopener">
39
- {{ item['bookmark-of'][0] | replace("https://", "") | replace("http://", "") | truncate(50) }}
45
+ <a href="{{ contextUrl }}" target="_blank" rel="noopener">
46
+ {{ contextUrl | replace("https://", "") | replace("http://", "") | truncate(50) }}
40
47
  </a>
41
48
  </div>
42
49
  {% endif %}