@feedlog-ai/webcomponents 0.0.31 → 0.0.33

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.
@@ -413,7 +413,12 @@ const FeedlogIssuesClient = class {
413
413
  this.previousLimit = this.limit;
414
414
  this.previousSortBy = this.sortBy;
415
415
  this.initializeSDK();
416
- this.fetchIssues();
416
+ // Return the promise so SSR waits for the fetch before serializing HTML.
417
+ // During client hydration, skip fetch if we already have server-rendered data.
418
+ if (this.issues.length > 0 && !this.loading) {
419
+ return;
420
+ }
421
+ return this.fetchIssues();
417
422
  }
418
423
  disconnectedCallback() {
419
424
  // Prevent any pending async operations from updating state
@@ -559,7 +564,7 @@ const FeedlogIssuesClient = class {
559
564
  const style = hostBg
560
565
  ? { '--feedlog-background': hostBg }
561
566
  : undefined;
562
- return (index.h("feedlog-issues", { key: '15ae96d7d7b51964026f873f8b97e10530ed02b4', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
567
+ return (index.h("feedlog-issues", { key: '0528f8bbb5f1e735c3b50a4325221ee8bfdfadda', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
563
568
  }
564
569
  get el() { return index.getElement(this); }
565
570
  };
@@ -73,7 +73,12 @@ export class FeedlogIssuesClient {
73
73
  this.previousLimit = this.limit;
74
74
  this.previousSortBy = this.sortBy;
75
75
  this.initializeSDK();
76
- this.fetchIssues();
76
+ // Return the promise so SSR waits for the fetch before serializing HTML.
77
+ // During client hydration, skip fetch if we already have server-rendered data.
78
+ if (this.issues.length > 0 && !this.loading) {
79
+ return;
80
+ }
81
+ return this.fetchIssues();
77
82
  }
78
83
  disconnectedCallback() {
79
84
  // Prevent any pending async operations from updating state
@@ -219,7 +224,7 @@ export class FeedlogIssuesClient {
219
224
  const style = hostBg
220
225
  ? { '--feedlog-background': hostBg }
221
226
  : undefined;
222
- return (h("feedlog-issues", { key: '15ae96d7d7b51964026f873f8b97e10530ed02b4', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
227
+ return (h("feedlog-issues", { key: '0528f8bbb5f1e735c3b50a4325221ee8bfdfadda', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
223
228
  }
224
229
  static get is() { return "feedlog-issues-client"; }
225
230
  static get encapsulation() { return "shadow"; }
@@ -1 +1 @@
1
- import{t,p as e,H as s,c as i,h as o}from"./index.js";import{d as r}from"./p-DzATWlAC.js";import{d as n}from"./p-DMdb-G26.js";import{d as h}from"./p-BRjVS8bz.js";import{d as a}from"./p-BBbiSGNf.js";import{d as u}from"./p-CuFKEckF.js";function l(t){if("string"!=typeof t)return"";let e=t.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"");return e=e.replace(/\s*on\w+\s*=\s*["'][^"']*["']/gi,""),e=e.replace(/\s*on\w+\s*=\s*[^\s>]*/gi,""),e=e.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi,""),e=e.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi,""),e=e.replace(/<(embed|object)\b[^<]*>/gi,""),e=e.replace(/javascript:/gi,""),e=e.replace(/data:(?!image\/(?:png|jpg|jpeg|gif|webp);)/gi,""),e}class d extends Error{constructor(t,e,s){super(t),this.statusCode=e,this.originalError=s,this.name="FeedlogError",Object.setPrototypeOf(this,d.prototype)}}class c extends d{constructor(t){super(t),this.name="FeedlogValidationError",Object.setPrototypeOf(this,c.prototype)}}class p extends d{constructor(t,e,s){super(t,e,s),this.name="FeedlogNetworkError",Object.setPrototypeOf(this,p.prototype)}}class f extends d{constructor(t="Request timed out"){super(t),this.name="FeedlogTimeoutError",Object.setPrototypeOf(this,f.prototype)}}class g{constructor(t){if(this.config={credentials:"include",...t},this.apiKey=this.config.apiKey,!this.apiKey)throw new c("apiKey is required in FeedlogSDKConfig");this.endpoint=this.config.endpoint||"https://api.feedlog.app",this.timeout=this.config.timeout||3e4,this.endpoint=this.endpoint.replace(/\/$/,"")}async fetchIssues(t={}){try{const e=this.buildIssuesUrl(t),s=await this.fetchWithTimeout(e,{method:"GET",headers:this.getAuthHeaders(),credentials:this.config.credentials||"include"});if(!s.ok)throw new p("Failed to fetch issues: "+s.statusText,s.status);const i=await s.json();return this.validateIssuesResponse(i)}catch(t){if(t instanceof d)throw t;if(t instanceof TypeError&&t.message.includes("fetch"))throw new p("Network error: Unable to reach API",void 0,t);throw new d("Failed to fetch issues: "+(t instanceof Error?t.message:"Unknown error"),void 0,t)}}async toggleUpvote(t){if(!t||"string"!=typeof t)throw new c("Issue ID is required");try{const e=`${this.endpoint}/api/issues/${encodeURIComponent(t)}/upvote`,s=await this.fetchWithTimeout(e,{method:"POST",headers:this.getAuthHeaders(),credentials:this.config.credentials||"include",body:JSON.stringify({})});if(404===s.status)throw new p("Issue not found",404);if(401===s.status)throw new p("Unauthorized",401);if(403===s.status)throw new p("Forbidden: Domain not allowed for this repository",403);if(!s.ok)throw new p("Failed to toggle upvote: "+s.statusText,s.status);const i=await s.json();return this.validateUpvoteResponse(i)}catch(t){if(t instanceof d)throw t;if(t instanceof TypeError&&t.message.includes("fetch"))throw new p("Network error: Unable to reach API",void 0,t);throw new d("Failed to toggle upvote: "+(t instanceof Error?t.message:"Unknown error"),void 0,t)}}buildIssuesUrl(t){const e=new URL(this.endpoint+"/api/issues");if(t.repositoryIds){const s=Array.isArray(t.repositoryIds)?t.repositoryIds:[t.repositoryIds];for(const t of s)e.searchParams.append("repositoryIds",t)}return t.type&&e.searchParams.set("type",t.type),t.sortBy&&e.searchParams.set("sortBy",t.sortBy),t.cursor&&e.searchParams.set("cursor",t.cursor),void 0!==t.limit&&e.searchParams.set("limit",""+t.limit),""+e}getAuthHeaders(){const t={"Content-Type":"application/json"};return this.apiKey&&(t["x-api-key"]=this.apiKey),t}async fetchWithTimeout(t,e){const s=new AbortController,i=setTimeout((()=>s.abort()),this.timeout);try{const o=await fetch(t,{...e,signal:s.signal});return clearTimeout(i),o}catch(t){if(clearTimeout(i),t instanceof Error&&"AbortError"===t.name)throw new f(`Request timed out after ${this.timeout}ms`);throw t}}validateIssuesResponse(t){if(!t||"object"!=typeof t)throw new c("Invalid API response: expected object");const e=t;if(!Array.isArray(e.issues))throw new c("Invalid API response: issues must be an array");if(!e.pagination||"object"!=typeof e.pagination)throw new c("Invalid API response: pagination is required");return{issues:e.issues.map((t=>this.validateIssue(t))),pagination:{cursor:e.pagination.cursor,hasMore:!!e.pagination.hasMore}}}validateIssue(t){if(!t||"object"!=typeof t)throw new c("Invalid issue: expected object");const e=t;if("string"!=typeof e.id)throw new c("Invalid issue: id is required and must be a string");if(!["bug","enhancement"].includes(e.type+""))throw new c('Invalid issue: type must be "bug" or "enhancement"');if(!["open","in_progress","closed"].includes(e.status+""))throw new c('Invalid issue: status must be "open", "in_progress", or "closed"');if(!e.repository||"object"!=typeof e.repository)throw new c("Invalid issue: repository is required");const s=e.repository;if("string"!=typeof s.id)throw new c("Invalid issue: repository must have id");const i=null!==e.githubIssueLink&&"string"==typeof e.githubIssueLink?e.githubIssueLink+"":null,o=e.title,r=null!=o&&""!==o?l(o+""):null,n=e.body,h=null!=n&&""!==n?l(n+""):null,a=s.name,u=null!=a&&""!==a?a+"":null,d=s.description,p=null!=d&&""!==d?l(d+""):null;return{id:e.id+"",githubIssueLink:i,type:e.type||"bug",status:e.status||"open",pinnedAt:e.pinnedAt?e.pinnedAt+"":null,revision:Number(e.revision)||1,title:r,body:h,repository:{id:s.id+"",name:u,description:p},updatedAt:e.updatedAt+""||(new Date).toISOString(),createdAt:e.createdAt+""||(new Date).toISOString(),upvoteCount:Number(e.upvoteCount)||0,hasUpvoted:!!e.hasUpvoted}}validateUpvoteResponse(t){if(!t||"object"!=typeof t)throw new c("Invalid upvote response: expected object");const e=t;if("boolean"!=typeof e.upvoted)throw new c("Invalid upvote response: upvoted must be a boolean");if("number"!=typeof e.upvoteCount)throw new c("Invalid upvote response: upvoteCount must be a number");if("string"!=typeof e.anonymousUserId)throw new c("Invalid upvote response: anonymousUserId must be a string");return{upvoted:e.upvoted,upvoteCount:e.upvoteCount,anonymousUserId:e.anonymousUserId}}getEndpoint(){return this.endpoint}getTimeout(){return this.timeout}}const m=e(class extends s{constructor(t){super(),!1!==t&&this.__registerHost(),this.__attachShadow(),this.feedlogUpvote=i(this,"feedlogUpvote"),this.feedlogError=i(this,"feedlogError"),this.maxWidth="42rem",this.theme="light",this.issues=[],this.loading=!0,this.error=null,this.cursor=null,this.hasMore=!1,this.isLoadingMore=!1,this.sdk=null,this.fetchRequestId=0,this.isDisconnected=!1,this.upvoteRequestIds=new Map,this.handleUpvote=async t=>{if(!this.sdk||this.isDisconnected)return;const{issueId:e,currentUpvoted:s,currentCount:i}=t.detail,o=(this.upvoteRequestIds.get(e)||0)+1;this.upvoteRequestIds.set(e,o),this.issues=this.issues.map((t=>t.id===e?Object.assign(Object.assign({},t),{hasUpvoted:!s,upvoteCount:s?i-1:i+1}):t));try{const t=await this.sdk.toggleUpvote(e);if(this.isDisconnected||this.upvoteRequestIds.get(e)!==o)return;this.issues=this.issues.map((s=>s.id===e?Object.assign(Object.assign({},s),{hasUpvoted:t.upvoted,upvoteCount:t.upvoteCount}):s)),this.feedlogUpvote.emit({issueId:e,upvoted:t.upvoted,upvoteCount:t.upvoteCount})}catch(t){if(this.isDisconnected||this.upvoteRequestIds.get(e)!==o)return;this.issues=this.issues.map((t=>t.id===e?Object.assign(Object.assign({},t),{hasUpvoted:s,upvoteCount:i}):t)),this.feedlogError.emit({error:t instanceof Error?t.message:"Failed to toggle upvote"})}}}componentWillLoad(){this.previousType=this.type,this.previousLimit=this.limit,this.previousSortBy=this.sortBy,this.initializeSDK(),this.fetchIssues()}disconnectedCallback(){this.isDisconnected=!0,this.fetchRequestId++}componentDidUpdate(){(this.previousType!==this.type||this.previousLimit!==this.limit||this.previousSortBy!==this.sortBy)&&(this.fetchRequestId++,this.cursor=null,this.hasMore=!1,this.issues=[],this.fetchIssues(),this.previousType=this.type,this.previousLimit=this.limit,this.previousSortBy=this.sortBy)}initializeSDK(){try{if(!this.apiKey)throw Error("API key is required for the Feedlog SDK");this.sdk=new g(Object.assign({apiKey:this.apiKey},this.endpoint&&{endpoint:this.endpoint})),this.error=null}catch(t){const e=t instanceof Error?t.message:"Failed to initialize SDK";this.error=e,this.feedlogError.emit({error:e})}}async fetchIssues(){if(!this.sdk)return;const t=this.fetchRequestId;try{this.loading=!0,this.error=null;const e={};this.type&&(e.type=this.type),this.sortBy&&(e.sortBy=this.sortBy),this.limit&&(e.limit=this.limit),this.cursor&&(e.cursor=this.cursor);const s=await this.sdk.fetchIssues(e);if(this.isDisconnected||t!==this.fetchRequestId)return;this.issues=s.issues,this.cursor=s.pagination.cursor,this.hasMore=s.pagination.hasMore}catch(e){if(this.isDisconnected||t!==this.fetchRequestId)return;const s=e instanceof Error?e.message:"Couldn't load updates";this.error=s,this.issues=[],this.feedlogError.emit({error:s,code:null==e?void 0:e.statusCode})}finally{this.isDisconnected||t!==this.fetchRequestId||(this.loading=!1,this.isLoadingMore=!1)}}async loadMore(){if(!this.sdk||!this.hasMore||this.isLoadingMore||this.loading)return;const t=this.fetchRequestId;this.isLoadingMore=!0;try{const e={};this.type&&(e.type=this.type),this.sortBy&&(e.sortBy=this.sortBy),this.limit&&(e.limit=this.limit),this.cursor&&(e.cursor=this.cursor);const s=await this.sdk.fetchIssues(e);if(this.isDisconnected||t!==this.fetchRequestId)return;this.issues=[...this.issues,...s.issues],this.cursor=s.pagination.cursor,this.hasMore=s.pagination.hasMore}catch(e){if(this.isDisconnected||t!==this.fetchRequestId)return;this.feedlogError.emit({error:e instanceof Error?e.message:"Failed to load more issues",code:null==e?void 0:e.statusCode})}finally{this.isDisconnected||t!==this.fetchRequestId||(this.isLoadingMore=!1)}}render(){var t,e;const s=null===(e=null===(t=this.el)||void 0===t?void 0:t.style)||void 0===e?void 0:e.getPropertyValue("--feedlog-background");return o("feedlog-issues",{key:"15ae96d7d7b51964026f873f8b97e10530ed02b4",style:s?{"--feedlog-background":s}:void 0,issues:this.issues,limit:this.limit,maxWidth:this.maxWidth,theme:this.theme,heading:this.heading,subtitle:this.subtitle,emptyStateTitle:this.emptyStateTitle,emptyStateMessage:this.emptyStateMessage,getIssueUrl:this.getIssueUrl,loading:this.loading,error:this.error,hasMore:this.hasMore,isLoadingMore:this.isLoadingMore,onFeedlogUpvote:this.handleUpvote,onFeedlogLoadMore:async()=>this.loadMore()})}get el(){return this}},[1,"feedlog-issues-client",{apiKey:[1,"api-key"],type:[1],limit:[2],sortBy:[1,"sort-by"],endpoint:[1],maxWidth:[1,"max-width"],theme:[1],heading:[1],subtitle:[1],emptyStateTitle:[1,"empty-state-title"],emptyStateMessage:[1,"empty-state-message"],getIssueUrl:[16],issues:[32],loading:[32],error:[32],cursor:[32],hasMore:[32],isLoadingMore:[32]}]);function w(){"undefined"!=typeof customElements&&["feedlog-issues-client","feedlog-badge","feedlog-button","feedlog-issue","feedlog-issues","feedlog-issues-list"].forEach((e=>{switch(e){case"feedlog-issues-client":customElements.get(t(e))||customElements.define(t(e),m);break;case"feedlog-badge":customElements.get(t(e))||r();break;case"feedlog-button":customElements.get(t(e))||n();break;case"feedlog-issue":customElements.get(t(e))||h();break;case"feedlog-issues":customElements.get(t(e))||a();break;case"feedlog-issues-list":customElements.get(t(e))||u()}}))}w();const b=m,y=w;export{b as FeedlogIssuesClient,y as defineCustomElement}
1
+ import{t,p as e,H as s,c as i,h as o}from"./index.js";import{d as r}from"./p-DzATWlAC.js";import{d as n}from"./p-DMdb-G26.js";import{d as h}from"./p-BRjVS8bz.js";import{d as a}from"./p-BBbiSGNf.js";import{d as u}from"./p-CuFKEckF.js";function l(t){if("string"!=typeof t)return"";let e=t.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"");return e=e.replace(/\s*on\w+\s*=\s*["'][^"']*["']/gi,""),e=e.replace(/\s*on\w+\s*=\s*[^\s>]*/gi,""),e=e.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi,""),e=e.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi,""),e=e.replace(/<(embed|object)\b[^<]*>/gi,""),e=e.replace(/javascript:/gi,""),e=e.replace(/data:(?!image\/(?:png|jpg|jpeg|gif|webp);)/gi,""),e}class c extends Error{constructor(t,e,s){super(t),this.statusCode=e,this.originalError=s,this.name="FeedlogError",Object.setPrototypeOf(this,c.prototype)}}class d extends c{constructor(t){super(t),this.name="FeedlogValidationError",Object.setPrototypeOf(this,d.prototype)}}class p extends c{constructor(t,e,s){super(t,e,s),this.name="FeedlogNetworkError",Object.setPrototypeOf(this,p.prototype)}}class f extends c{constructor(t="Request timed out"){super(t),this.name="FeedlogTimeoutError",Object.setPrototypeOf(this,f.prototype)}}class g{constructor(t){if(this.config={credentials:"include",...t},this.apiKey=this.config.apiKey,!this.apiKey)throw new d("apiKey is required in FeedlogSDKConfig");this.endpoint=this.config.endpoint||"https://api.feedlog.app",this.timeout=this.config.timeout||3e4,this.endpoint=this.endpoint.replace(/\/$/,"")}async fetchIssues(t={}){try{const e=this.buildIssuesUrl(t),s=await this.fetchWithTimeout(e,{method:"GET",headers:this.getAuthHeaders(),credentials:this.config.credentials||"include"});if(!s.ok)throw new p("Failed to fetch issues: "+s.statusText,s.status);const i=await s.json();return this.validateIssuesResponse(i)}catch(t){if(t instanceof c)throw t;if(t instanceof TypeError&&t.message.includes("fetch"))throw new p("Network error: Unable to reach API",void 0,t);throw new c("Failed to fetch issues: "+(t instanceof Error?t.message:"Unknown error"),void 0,t)}}async toggleUpvote(t){if(!t||"string"!=typeof t)throw new d("Issue ID is required");try{const e=`${this.endpoint}/api/issues/${encodeURIComponent(t)}/upvote`,s=await this.fetchWithTimeout(e,{method:"POST",headers:this.getAuthHeaders(),credentials:this.config.credentials||"include",body:JSON.stringify({})});if(404===s.status)throw new p("Issue not found",404);if(401===s.status)throw new p("Unauthorized",401);if(403===s.status)throw new p("Forbidden: Domain not allowed for this repository",403);if(!s.ok)throw new p("Failed to toggle upvote: "+s.statusText,s.status);const i=await s.json();return this.validateUpvoteResponse(i)}catch(t){if(t instanceof c)throw t;if(t instanceof TypeError&&t.message.includes("fetch"))throw new p("Network error: Unable to reach API",void 0,t);throw new c("Failed to toggle upvote: "+(t instanceof Error?t.message:"Unknown error"),void 0,t)}}buildIssuesUrl(t){const e=new URL(this.endpoint+"/api/issues");if(t.repositoryIds){const s=Array.isArray(t.repositoryIds)?t.repositoryIds:[t.repositoryIds];for(const t of s)e.searchParams.append("repositoryIds",t)}return t.type&&e.searchParams.set("type",t.type),t.sortBy&&e.searchParams.set("sortBy",t.sortBy),t.cursor&&e.searchParams.set("cursor",t.cursor),void 0!==t.limit&&e.searchParams.set("limit",""+t.limit),""+e}getAuthHeaders(){const t={"Content-Type":"application/json"};return this.apiKey&&(t["x-api-key"]=this.apiKey),t}async fetchWithTimeout(t,e){const s=new AbortController,i=setTimeout((()=>s.abort()),this.timeout);try{const o=await fetch(t,{...e,signal:s.signal});return clearTimeout(i),o}catch(t){if(clearTimeout(i),t instanceof Error&&"AbortError"===t.name)throw new f(`Request timed out after ${this.timeout}ms`);throw t}}validateIssuesResponse(t){if(!t||"object"!=typeof t)throw new d("Invalid API response: expected object");const e=t;if(!Array.isArray(e.issues))throw new d("Invalid API response: issues must be an array");if(!e.pagination||"object"!=typeof e.pagination)throw new d("Invalid API response: pagination is required");return{issues:e.issues.map((t=>this.validateIssue(t))),pagination:{cursor:e.pagination.cursor,hasMore:!!e.pagination.hasMore}}}validateIssue(t){if(!t||"object"!=typeof t)throw new d("Invalid issue: expected object");const e=t;if("string"!=typeof e.id)throw new d("Invalid issue: id is required and must be a string");if(!["bug","enhancement"].includes(e.type+""))throw new d('Invalid issue: type must be "bug" or "enhancement"');if(!["open","in_progress","closed"].includes(e.status+""))throw new d('Invalid issue: status must be "open", "in_progress", or "closed"');if(!e.repository||"object"!=typeof e.repository)throw new d("Invalid issue: repository is required");const s=e.repository;if("string"!=typeof s.id)throw new d("Invalid issue: repository must have id");const i=null!==e.githubIssueLink&&"string"==typeof e.githubIssueLink?e.githubIssueLink+"":null,o=e.title,r=null!=o&&""!==o?l(o+""):null,n=e.body,h=null!=n&&""!==n?l(n+""):null,a=s.name,u=null!=a&&""!==a?a+"":null,c=s.description,p=null!=c&&""!==c?l(c+""):null;return{id:e.id+"",githubIssueLink:i,type:e.type||"bug",status:e.status||"open",pinnedAt:e.pinnedAt?e.pinnedAt+"":null,revision:Number(e.revision)||1,title:r,body:h,repository:{id:s.id+"",name:u,description:p},updatedAt:e.updatedAt+""||(new Date).toISOString(),createdAt:e.createdAt+""||(new Date).toISOString(),upvoteCount:Number(e.upvoteCount)||0,hasUpvoted:!!e.hasUpvoted}}validateUpvoteResponse(t){if(!t||"object"!=typeof t)throw new d("Invalid upvote response: expected object");const e=t;if("boolean"!=typeof e.upvoted)throw new d("Invalid upvote response: upvoted must be a boolean");if("number"!=typeof e.upvoteCount)throw new d("Invalid upvote response: upvoteCount must be a number");if("string"!=typeof e.anonymousUserId)throw new d("Invalid upvote response: anonymousUserId must be a string");return{upvoted:e.upvoted,upvoteCount:e.upvoteCount,anonymousUserId:e.anonymousUserId}}getEndpoint(){return this.endpoint}getTimeout(){return this.timeout}}const m=e(class extends s{constructor(t){super(),!1!==t&&this.__registerHost(),this.__attachShadow(),this.feedlogUpvote=i(this,"feedlogUpvote"),this.feedlogError=i(this,"feedlogError"),this.maxWidth="42rem",this.theme="light",this.issues=[],this.loading=!0,this.error=null,this.cursor=null,this.hasMore=!1,this.isLoadingMore=!1,this.sdk=null,this.fetchRequestId=0,this.isDisconnected=!1,this.upvoteRequestIds=new Map,this.handleUpvote=async t=>{if(!this.sdk||this.isDisconnected)return;const{issueId:e,currentUpvoted:s,currentCount:i}=t.detail,o=(this.upvoteRequestIds.get(e)||0)+1;this.upvoteRequestIds.set(e,o),this.issues=this.issues.map((t=>t.id===e?Object.assign(Object.assign({},t),{hasUpvoted:!s,upvoteCount:s?i-1:i+1}):t));try{const t=await this.sdk.toggleUpvote(e);if(this.isDisconnected||this.upvoteRequestIds.get(e)!==o)return;this.issues=this.issues.map((s=>s.id===e?Object.assign(Object.assign({},s),{hasUpvoted:t.upvoted,upvoteCount:t.upvoteCount}):s)),this.feedlogUpvote.emit({issueId:e,upvoted:t.upvoted,upvoteCount:t.upvoteCount})}catch(t){if(this.isDisconnected||this.upvoteRequestIds.get(e)!==o)return;this.issues=this.issues.map((t=>t.id===e?Object.assign(Object.assign({},t),{hasUpvoted:s,upvoteCount:i}):t)),this.feedlogError.emit({error:t instanceof Error?t.message:"Failed to toggle upvote"})}}}componentWillLoad(){if(this.previousType=this.type,this.previousLimit=this.limit,this.previousSortBy=this.sortBy,this.initializeSDK(),!(this.issues.length>0)||this.loading)return this.fetchIssues()}disconnectedCallback(){this.isDisconnected=!0,this.fetchRequestId++}componentDidUpdate(){(this.previousType!==this.type||this.previousLimit!==this.limit||this.previousSortBy!==this.sortBy)&&(this.fetchRequestId++,this.cursor=null,this.hasMore=!1,this.issues=[],this.fetchIssues(),this.previousType=this.type,this.previousLimit=this.limit,this.previousSortBy=this.sortBy)}initializeSDK(){try{if(!this.apiKey)throw Error("API key is required for the Feedlog SDK");this.sdk=new g(Object.assign({apiKey:this.apiKey},this.endpoint&&{endpoint:this.endpoint})),this.error=null}catch(t){const e=t instanceof Error?t.message:"Failed to initialize SDK";this.error=e,this.feedlogError.emit({error:e})}}async fetchIssues(){if(!this.sdk)return;const t=this.fetchRequestId;try{this.loading=!0,this.error=null;const e={};this.type&&(e.type=this.type),this.sortBy&&(e.sortBy=this.sortBy),this.limit&&(e.limit=this.limit),this.cursor&&(e.cursor=this.cursor);const s=await this.sdk.fetchIssues(e);if(this.isDisconnected||t!==this.fetchRequestId)return;this.issues=s.issues,this.cursor=s.pagination.cursor,this.hasMore=s.pagination.hasMore}catch(e){if(this.isDisconnected||t!==this.fetchRequestId)return;const s=e instanceof Error?e.message:"Couldn't load updates";this.error=s,this.issues=[],this.feedlogError.emit({error:s,code:null==e?void 0:e.statusCode})}finally{this.isDisconnected||t!==this.fetchRequestId||(this.loading=!1,this.isLoadingMore=!1)}}async loadMore(){if(!this.sdk||!this.hasMore||this.isLoadingMore||this.loading)return;const t=this.fetchRequestId;this.isLoadingMore=!0;try{const e={};this.type&&(e.type=this.type),this.sortBy&&(e.sortBy=this.sortBy),this.limit&&(e.limit=this.limit),this.cursor&&(e.cursor=this.cursor);const s=await this.sdk.fetchIssues(e);if(this.isDisconnected||t!==this.fetchRequestId)return;this.issues=[...this.issues,...s.issues],this.cursor=s.pagination.cursor,this.hasMore=s.pagination.hasMore}catch(e){if(this.isDisconnected||t!==this.fetchRequestId)return;this.feedlogError.emit({error:e instanceof Error?e.message:"Failed to load more issues",code:null==e?void 0:e.statusCode})}finally{this.isDisconnected||t!==this.fetchRequestId||(this.isLoadingMore=!1)}}render(){var t,e;const s=null===(e=null===(t=this.el)||void 0===t?void 0:t.style)||void 0===e?void 0:e.getPropertyValue("--feedlog-background");return o("feedlog-issues",{key:"0528f8bbb5f1e735c3b50a4325221ee8bfdfadda",style:s?{"--feedlog-background":s}:void 0,issues:this.issues,limit:this.limit,maxWidth:this.maxWidth,theme:this.theme,heading:this.heading,subtitle:this.subtitle,emptyStateTitle:this.emptyStateTitle,emptyStateMessage:this.emptyStateMessage,getIssueUrl:this.getIssueUrl,loading:this.loading,error:this.error,hasMore:this.hasMore,isLoadingMore:this.isLoadingMore,onFeedlogUpvote:this.handleUpvote,onFeedlogLoadMore:async()=>this.loadMore()})}get el(){return this}},[1,"feedlog-issues-client",{apiKey:[1,"api-key"],type:[1],limit:[2],sortBy:[1,"sort-by"],endpoint:[1],maxWidth:[1,"max-width"],theme:[1],heading:[1],subtitle:[1],emptyStateTitle:[1,"empty-state-title"],emptyStateMessage:[1,"empty-state-message"],getIssueUrl:[16],issues:[32],loading:[32],error:[32],cursor:[32],hasMore:[32],isLoadingMore:[32]}]);function w(){"undefined"!=typeof customElements&&["feedlog-issues-client","feedlog-badge","feedlog-button","feedlog-issue","feedlog-issues","feedlog-issues-list"].forEach((e=>{switch(e){case"feedlog-issues-client":customElements.get(t(e))||customElements.define(t(e),m);break;case"feedlog-badge":customElements.get(t(e))||r();break;case"feedlog-button":customElements.get(t(e))||n();break;case"feedlog-issue":customElements.get(t(e))||h();break;case"feedlog-issues":customElements.get(t(e))||a();break;case"feedlog-issues-list":customElements.get(t(e))||u()}}))}w();const b=m,y=w;export{b as FeedlogIssuesClient,y as defineCustomElement}
@@ -411,7 +411,12 @@ const FeedlogIssuesClient = class {
411
411
  this.previousLimit = this.limit;
412
412
  this.previousSortBy = this.sortBy;
413
413
  this.initializeSDK();
414
- this.fetchIssues();
414
+ // Return the promise so SSR waits for the fetch before serializing HTML.
415
+ // During client hydration, skip fetch if we already have server-rendered data.
416
+ if (this.issues.length > 0 && !this.loading) {
417
+ return;
418
+ }
419
+ return this.fetchIssues();
415
420
  }
416
421
  disconnectedCallback() {
417
422
  // Prevent any pending async operations from updating state
@@ -557,7 +562,7 @@ const FeedlogIssuesClient = class {
557
562
  const style = hostBg
558
563
  ? { '--feedlog-background': hostBg }
559
564
  : undefined;
560
- return (h("feedlog-issues", { key: '15ae96d7d7b51964026f873f8b97e10530ed02b4', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
565
+ return (h("feedlog-issues", { key: '0528f8bbb5f1e735c3b50a4325221ee8bfdfadda', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
561
566
  }
562
567
  get el() { return getElement(this); }
563
568
  };
@@ -1 +1 @@
1
- import{p as e,g as t,b as a}from"./p-CgNWSmzU.js";export{s as setNonce}from"./p-CgNWSmzU.js";(()=>{const t=import.meta.url,s={};return""!==t&&(s.resourcesUrl=new URL(".",t).href),e(s)})().then((async e=>(await t(),a([["p-32663f65",[[257,"feedlog-card"]]],["p-da0268a8",[[257,"feedlog-badge",{variant:[1]}]]],["p-891c349f",[[1,"feedlog-issues-list",{issues:[16],limit:[2],theme:[1],getIssueUrl:[16],emptyStateTitle:[1,"empty-state-title"],emptyStateMessage:[1,"empty-state-message"],currentPage:[32]},null,{issues:[{resetPage:0}],limit:[{resetPage:0}]}],[257,"feedlog-button",{variant:[1],size:[1],disabled:[4],type:[1]}],[257,"feedlog-issue",{issue:[16],issueUrl:[1,"issue-url"],theme:[1]}]]],["p-f22117ae",[[1,"feedlog-issues",{issues:[16],maxWidth:[1,"max-width"],limit:[2],theme:[1025],heading:[1],subtitle:[1],emptyStateTitle:[1,"empty-state-title"],emptyStateMessage:[1,"empty-state-message"],loading:[4],error:[1],hasMore:[4,"has-more"],isLoadingMore:[4,"is-loading-more"],getIssueUrl:[16],currentTheme:[32]}]]],["p-c3e9b94b",[[1,"feedlog-issues-client",{apiKey:[1,"api-key"],type:[1],limit:[2],sortBy:[1,"sort-by"],endpoint:[1],maxWidth:[1,"max-width"],theme:[1],heading:[1],subtitle:[1],emptyStateTitle:[1,"empty-state-title"],emptyStateMessage:[1,"empty-state-message"],getIssueUrl:[16],issues:[32],loading:[32],error:[32],cursor:[32],hasMore:[32],isLoadingMore:[32]}]]]],e))));
1
+ import{p as e,g as t,b as a}from"./p-CgNWSmzU.js";export{s as setNonce}from"./p-CgNWSmzU.js";(()=>{const t=import.meta.url,s={};return""!==t&&(s.resourcesUrl=new URL(".",t).href),e(s)})().then((async e=>(await t(),a([["p-32663f65",[[257,"feedlog-card"]]],["p-da0268a8",[[257,"feedlog-badge",{variant:[1]}]]],["p-891c349f",[[1,"feedlog-issues-list",{issues:[16],limit:[2],theme:[1],getIssueUrl:[16],emptyStateTitle:[1,"empty-state-title"],emptyStateMessage:[1,"empty-state-message"],currentPage:[32]},null,{issues:[{resetPage:0}],limit:[{resetPage:0}]}],[257,"feedlog-button",{variant:[1],size:[1],disabled:[4],type:[1]}],[257,"feedlog-issue",{issue:[16],issueUrl:[1,"issue-url"],theme:[1]}]]],["p-f22117ae",[[1,"feedlog-issues",{issues:[16],maxWidth:[1,"max-width"],limit:[2],theme:[1025],heading:[1],subtitle:[1],emptyStateTitle:[1,"empty-state-title"],emptyStateMessage:[1,"empty-state-message"],loading:[4],error:[1],hasMore:[4,"has-more"],isLoadingMore:[4,"is-loading-more"],getIssueUrl:[16],currentTheme:[32]}]]],["p-b51c95a6",[[1,"feedlog-issues-client",{apiKey:[1,"api-key"],type:[1],limit:[2],sortBy:[1,"sort-by"],endpoint:[1],maxWidth:[1,"max-width"],theme:[1],heading:[1],subtitle:[1],emptyStateTitle:[1,"empty-state-title"],emptyStateMessage:[1,"empty-state-message"],getIssueUrl:[16],issues:[32],loading:[32],error:[32],cursor:[32],hasMore:[32],isLoadingMore:[32]}]]]],e))));
@@ -1 +1 @@
1
- import{r as t,c as s,h as e,a as i}from"./p-CgNWSmzU.js";function o(t){if("string"!=typeof t)return"";let s=t.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"");return s=s.replace(/\s*on\w+\s*=\s*["'][^"']*["']/gi,""),s=s.replace(/\s*on\w+\s*=\s*[^\s>]*/gi,""),s=s.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi,""),s=s.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi,""),s=s.replace(/<(embed|object)\b[^<]*>/gi,""),s=s.replace(/javascript:/gi,""),s=s.replace(/data:(?!image\/(?:png|jpg|jpeg|gif|webp);)/gi,""),s}class r extends Error{constructor(t,s,e){super(t),this.statusCode=s,this.originalError=e,this.name="FeedlogError",Object.setPrototypeOf(this,r.prototype)}}class n extends r{constructor(t){super(t),this.name="FeedlogValidationError",Object.setPrototypeOf(this,n.prototype)}}class h extends r{constructor(t,s,e){super(t,s,e),this.name="FeedlogNetworkError",Object.setPrototypeOf(this,h.prototype)}}class a extends r{constructor(t="Request timed out"){super(t),this.name="FeedlogTimeoutError",Object.setPrototypeOf(this,a.prototype)}}class u{constructor(t){if(this.config={credentials:"include",...t},this.apiKey=this.config.apiKey,!this.apiKey)throw new n("apiKey is required in FeedlogSDKConfig");this.endpoint=this.config.endpoint||"https://api.feedlog.app",this.timeout=this.config.timeout||3e4,this.endpoint=this.endpoint.replace(/\/$/,"")}async fetchIssues(t={}){try{const s=this.buildIssuesUrl(t),e=await this.fetchWithTimeout(s,{method:"GET",headers:this.getAuthHeaders(),credentials:this.config.credentials||"include"});if(!e.ok)throw new h(`Failed to fetch issues: ${e.statusText}`,e.status);const i=await e.json();return this.validateIssuesResponse(i)}catch(t){if(t instanceof r)throw t;if(t instanceof TypeError&&t.message.includes("fetch"))throw new h("Network error: Unable to reach API",void 0,t);throw new r(`Failed to fetch issues: ${t instanceof Error?t.message:"Unknown error"}`,void 0,t)}}async toggleUpvote(t){if(!t||"string"!=typeof t)throw new n("Issue ID is required");try{const s=`${this.endpoint}/api/issues/${encodeURIComponent(t)}/upvote`,e=await this.fetchWithTimeout(s,{method:"POST",headers:this.getAuthHeaders(),credentials:this.config.credentials||"include",body:JSON.stringify({})});if(404===e.status)throw new h("Issue not found",404);if(401===e.status)throw new h("Unauthorized",401);if(403===e.status)throw new h("Forbidden: Domain not allowed for this repository",403);if(!e.ok)throw new h(`Failed to toggle upvote: ${e.statusText}`,e.status);const i=await e.json();return this.validateUpvoteResponse(i)}catch(t){if(t instanceof r)throw t;if(t instanceof TypeError&&t.message.includes("fetch"))throw new h("Network error: Unable to reach API",void 0,t);throw new r(`Failed to toggle upvote: ${t instanceof Error?t.message:"Unknown error"}`,void 0,t)}}buildIssuesUrl(t){const s=new URL(`${this.endpoint}/api/issues`);if(t.repositoryIds){const e=Array.isArray(t.repositoryIds)?t.repositoryIds:[t.repositoryIds];for(const t of e)s.searchParams.append("repositoryIds",t)}return t.type&&s.searchParams.set("type",t.type),t.sortBy&&s.searchParams.set("sortBy",t.sortBy),t.cursor&&s.searchParams.set("cursor",t.cursor),void 0!==t.limit&&s.searchParams.set("limit",t.limit.toString()),s.toString()}getAuthHeaders(){const t={"Content-Type":"application/json"};return this.apiKey&&(t["x-api-key"]=this.apiKey),t}async fetchWithTimeout(t,s){const e=new AbortController,i=setTimeout((()=>e.abort()),this.timeout);try{const o=await fetch(t,{...s,signal:e.signal});return clearTimeout(i),o}catch(t){if(clearTimeout(i),t instanceof Error&&"AbortError"===t.name)throw new a(`Request timed out after ${this.timeout}ms`);throw t}}validateIssuesResponse(t){if(!t||"object"!=typeof t)throw new n("Invalid API response: expected object");const s=t;if(!Array.isArray(s.issues))throw new n("Invalid API response: issues must be an array");if(!s.pagination||"object"!=typeof s.pagination)throw new n("Invalid API response: pagination is required");return{issues:s.issues.map((t=>this.validateIssue(t))),pagination:{cursor:s.pagination.cursor,hasMore:Boolean(s.pagination.hasMore)}}}validateIssue(t){if(!t||"object"!=typeof t)throw new n("Invalid issue: expected object");const s=t;if("string"!=typeof s.id)throw new n("Invalid issue: id is required and must be a string");if(!["bug","enhancement"].includes(String(s.type)))throw new n('Invalid issue: type must be "bug" or "enhancement"');if(!["open","in_progress","closed"].includes(String(s.status)))throw new n('Invalid issue: status must be "open", "in_progress", or "closed"');if(!s.repository||"object"!=typeof s.repository)throw new n("Invalid issue: repository is required");const e=s.repository;if("string"!=typeof e.id)throw new n("Invalid issue: repository must have id");const i=null!==s.githubIssueLink&&"string"==typeof s.githubIssueLink?String(s.githubIssueLink):null,r=s.title,h=null!=r&&""!==r?o(String(r)):null,a=s.body,u=null!=a&&""!==a?o(String(a)):null,c=e.name,l=null!=c&&""!==c?String(c):null,d=e.description,p=null!=d&&""!==d?o(String(d)):null;return{id:String(s.id),githubIssueLink:i,type:s.type||"bug",status:s.status||"open",pinnedAt:s.pinnedAt?String(s.pinnedAt):null,revision:Number(s.revision)||1,title:h,body:u,repository:{id:String(e.id),name:l,description:p},updatedAt:String(s.updatedAt)||(new Date).toISOString(),createdAt:String(s.createdAt)||(new Date).toISOString(),upvoteCount:Number(s.upvoteCount)||0,hasUpvoted:Boolean(s.hasUpvoted)}}validateUpvoteResponse(t){if(!t||"object"!=typeof t)throw new n("Invalid upvote response: expected object");const s=t;if("boolean"!=typeof s.upvoted)throw new n("Invalid upvote response: upvoted must be a boolean");if("number"!=typeof s.upvoteCount)throw new n("Invalid upvote response: upvoteCount must be a number");if("string"!=typeof s.anonymousUserId)throw new n("Invalid upvote response: anonymousUserId must be a string");return{upvoted:s.upvoted,upvoteCount:s.upvoteCount,anonymousUserId:s.anonymousUserId}}getEndpoint(){return this.endpoint}getTimeout(){return this.timeout}}const c=class{constructor(e){t(this,e),this.feedlogUpvote=s(this,"feedlogUpvote"),this.feedlogError=s(this,"feedlogError"),this.maxWidth="42rem",this.theme="light",this.issues=[],this.loading=!0,this.error=null,this.cursor=null,this.hasMore=!1,this.isLoadingMore=!1,this.sdk=null,this.fetchRequestId=0,this.isDisconnected=!1,this.upvoteRequestIds=new Map,this.handleUpvote=async t=>{if(!this.sdk||this.isDisconnected)return;const{issueId:s,currentUpvoted:e,currentCount:i}=t.detail,o=(this.upvoteRequestIds.get(s)||0)+1;this.upvoteRequestIds.set(s,o),this.issues=this.issues.map((t=>t.id===s?Object.assign(Object.assign({},t),{hasUpvoted:!e,upvoteCount:e?i-1:i+1}):t));try{const t=await this.sdk.toggleUpvote(s);if(this.isDisconnected||this.upvoteRequestIds.get(s)!==o)return;this.issues=this.issues.map((e=>e.id===s?Object.assign(Object.assign({},e),{hasUpvoted:t.upvoted,upvoteCount:t.upvoteCount}):e)),this.feedlogUpvote.emit({issueId:s,upvoted:t.upvoted,upvoteCount:t.upvoteCount})}catch(t){if(this.isDisconnected||this.upvoteRequestIds.get(s)!==o)return;this.issues=this.issues.map((t=>t.id===s?Object.assign(Object.assign({},t),{hasUpvoted:e,upvoteCount:i}):t));const r=t instanceof Error?t.message:"Failed to toggle upvote";this.feedlogError.emit({error:r})}}}componentWillLoad(){this.previousType=this.type,this.previousLimit=this.limit,this.previousSortBy=this.sortBy,this.initializeSDK(),this.fetchIssues()}disconnectedCallback(){this.isDisconnected=!0,this.fetchRequestId++}componentDidUpdate(){(this.previousType!==this.type||this.previousLimit!==this.limit||this.previousSortBy!==this.sortBy)&&(this.fetchRequestId++,this.cursor=null,this.hasMore=!1,this.issues=[],this.fetchIssues(),this.previousType=this.type,this.previousLimit=this.limit,this.previousSortBy=this.sortBy)}initializeSDK(){try{if(!this.apiKey)throw new Error("API key is required for the Feedlog SDK");this.sdk=new u(Object.assign({apiKey:this.apiKey},this.endpoint&&{endpoint:this.endpoint})),this.error=null}catch(t){const s=t instanceof Error?t.message:"Failed to initialize SDK";this.error=s,this.feedlogError.emit({error:s})}}async fetchIssues(){if(!this.sdk)return;const t=this.fetchRequestId;try{this.loading=!0,this.error=null;const s={};this.type&&(s.type=this.type),this.sortBy&&(s.sortBy=this.sortBy),this.limit&&(s.limit=this.limit),this.cursor&&(s.cursor=this.cursor);const e=await this.sdk.fetchIssues(s);if(this.isDisconnected||t!==this.fetchRequestId)return;this.issues=e.issues,this.cursor=e.pagination.cursor,this.hasMore=e.pagination.hasMore}catch(s){if(this.isDisconnected||t!==this.fetchRequestId)return;const e=s instanceof Error?s.message:"Couldn't load updates";this.error=e,this.issues=[],this.feedlogError.emit({error:e,code:null==s?void 0:s.statusCode})}finally{this.isDisconnected||t!==this.fetchRequestId||(this.loading=!1,this.isLoadingMore=!1)}}async loadMore(){if(!this.sdk||!this.hasMore||this.isLoadingMore||this.loading)return;const t=this.fetchRequestId;this.isLoadingMore=!0;try{const s={};this.type&&(s.type=this.type),this.sortBy&&(s.sortBy=this.sortBy),this.limit&&(s.limit=this.limit),this.cursor&&(s.cursor=this.cursor);const e=await this.sdk.fetchIssues(s);if(this.isDisconnected||t!==this.fetchRequestId)return;this.issues=[...this.issues,...e.issues],this.cursor=e.pagination.cursor,this.hasMore=e.pagination.hasMore}catch(s){if(this.isDisconnected||t!==this.fetchRequestId)return;const e=s instanceof Error?s.message:"Failed to load more issues";this.feedlogError.emit({error:e,code:null==s?void 0:s.statusCode})}finally{this.isDisconnected||t!==this.fetchRequestId||(this.isLoadingMore=!1)}}render(){var t,s;const i=null===(s=null===(t=this.el)||void 0===t?void 0:t.style)||void 0===s?void 0:s.getPropertyValue("--feedlog-background");return e("feedlog-issues",{key:"15ae96d7d7b51964026f873f8b97e10530ed02b4",style:i?{"--feedlog-background":i}:void 0,issues:this.issues,limit:this.limit,maxWidth:this.maxWidth,theme:this.theme,heading:this.heading,subtitle:this.subtitle,emptyStateTitle:this.emptyStateTitle,emptyStateMessage:this.emptyStateMessage,getIssueUrl:this.getIssueUrl,loading:this.loading,error:this.error,hasMore:this.hasMore,isLoadingMore:this.isLoadingMore,onFeedlogUpvote:this.handleUpvote,onFeedlogLoadMore:async()=>this.loadMore()})}get el(){return i(this)}};export{c as feedlog_issues_client}
1
+ import{r as t,c as s,h as e,a as i}from"./p-CgNWSmzU.js";function o(t){if("string"!=typeof t)return"";let s=t.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"");return s=s.replace(/\s*on\w+\s*=\s*["'][^"']*["']/gi,""),s=s.replace(/\s*on\w+\s*=\s*[^\s>]*/gi,""),s=s.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi,""),s=s.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi,""),s=s.replace(/<(embed|object)\b[^<]*>/gi,""),s=s.replace(/javascript:/gi,""),s=s.replace(/data:(?!image\/(?:png|jpg|jpeg|gif|webp);)/gi,""),s}class r extends Error{constructor(t,s,e){super(t),this.statusCode=s,this.originalError=e,this.name="FeedlogError",Object.setPrototypeOf(this,r.prototype)}}class n extends r{constructor(t){super(t),this.name="FeedlogValidationError",Object.setPrototypeOf(this,n.prototype)}}class h extends r{constructor(t,s,e){super(t,s,e),this.name="FeedlogNetworkError",Object.setPrototypeOf(this,h.prototype)}}class a extends r{constructor(t="Request timed out"){super(t),this.name="FeedlogTimeoutError",Object.setPrototypeOf(this,a.prototype)}}class u{constructor(t){if(this.config={credentials:"include",...t},this.apiKey=this.config.apiKey,!this.apiKey)throw new n("apiKey is required in FeedlogSDKConfig");this.endpoint=this.config.endpoint||"https://api.feedlog.app",this.timeout=this.config.timeout||3e4,this.endpoint=this.endpoint.replace(/\/$/,"")}async fetchIssues(t={}){try{const s=this.buildIssuesUrl(t),e=await this.fetchWithTimeout(s,{method:"GET",headers:this.getAuthHeaders(),credentials:this.config.credentials||"include"});if(!e.ok)throw new h(`Failed to fetch issues: ${e.statusText}`,e.status);const i=await e.json();return this.validateIssuesResponse(i)}catch(t){if(t instanceof r)throw t;if(t instanceof TypeError&&t.message.includes("fetch"))throw new h("Network error: Unable to reach API",void 0,t);throw new r(`Failed to fetch issues: ${t instanceof Error?t.message:"Unknown error"}`,void 0,t)}}async toggleUpvote(t){if(!t||"string"!=typeof t)throw new n("Issue ID is required");try{const s=`${this.endpoint}/api/issues/${encodeURIComponent(t)}/upvote`,e=await this.fetchWithTimeout(s,{method:"POST",headers:this.getAuthHeaders(),credentials:this.config.credentials||"include",body:JSON.stringify({})});if(404===e.status)throw new h("Issue not found",404);if(401===e.status)throw new h("Unauthorized",401);if(403===e.status)throw new h("Forbidden: Domain not allowed for this repository",403);if(!e.ok)throw new h(`Failed to toggle upvote: ${e.statusText}`,e.status);const i=await e.json();return this.validateUpvoteResponse(i)}catch(t){if(t instanceof r)throw t;if(t instanceof TypeError&&t.message.includes("fetch"))throw new h("Network error: Unable to reach API",void 0,t);throw new r(`Failed to toggle upvote: ${t instanceof Error?t.message:"Unknown error"}`,void 0,t)}}buildIssuesUrl(t){const s=new URL(`${this.endpoint}/api/issues`);if(t.repositoryIds){const e=Array.isArray(t.repositoryIds)?t.repositoryIds:[t.repositoryIds];for(const t of e)s.searchParams.append("repositoryIds",t)}return t.type&&s.searchParams.set("type",t.type),t.sortBy&&s.searchParams.set("sortBy",t.sortBy),t.cursor&&s.searchParams.set("cursor",t.cursor),void 0!==t.limit&&s.searchParams.set("limit",t.limit.toString()),s.toString()}getAuthHeaders(){const t={"Content-Type":"application/json"};return this.apiKey&&(t["x-api-key"]=this.apiKey),t}async fetchWithTimeout(t,s){const e=new AbortController,i=setTimeout((()=>e.abort()),this.timeout);try{const o=await fetch(t,{...s,signal:e.signal});return clearTimeout(i),o}catch(t){if(clearTimeout(i),t instanceof Error&&"AbortError"===t.name)throw new a(`Request timed out after ${this.timeout}ms`);throw t}}validateIssuesResponse(t){if(!t||"object"!=typeof t)throw new n("Invalid API response: expected object");const s=t;if(!Array.isArray(s.issues))throw new n("Invalid API response: issues must be an array");if(!s.pagination||"object"!=typeof s.pagination)throw new n("Invalid API response: pagination is required");return{issues:s.issues.map((t=>this.validateIssue(t))),pagination:{cursor:s.pagination.cursor,hasMore:Boolean(s.pagination.hasMore)}}}validateIssue(t){if(!t||"object"!=typeof t)throw new n("Invalid issue: expected object");const s=t;if("string"!=typeof s.id)throw new n("Invalid issue: id is required and must be a string");if(!["bug","enhancement"].includes(String(s.type)))throw new n('Invalid issue: type must be "bug" or "enhancement"');if(!["open","in_progress","closed"].includes(String(s.status)))throw new n('Invalid issue: status must be "open", "in_progress", or "closed"');if(!s.repository||"object"!=typeof s.repository)throw new n("Invalid issue: repository is required");const e=s.repository;if("string"!=typeof e.id)throw new n("Invalid issue: repository must have id");const i=null!==s.githubIssueLink&&"string"==typeof s.githubIssueLink?String(s.githubIssueLink):null,r=s.title,h=null!=r&&""!==r?o(String(r)):null,a=s.body,u=null!=a&&""!==a?o(String(a)):null,c=e.name,l=null!=c&&""!==c?String(c):null,d=e.description,p=null!=d&&""!==d?o(String(d)):null;return{id:String(s.id),githubIssueLink:i,type:s.type||"bug",status:s.status||"open",pinnedAt:s.pinnedAt?String(s.pinnedAt):null,revision:Number(s.revision)||1,title:h,body:u,repository:{id:String(e.id),name:l,description:p},updatedAt:String(s.updatedAt)||(new Date).toISOString(),createdAt:String(s.createdAt)||(new Date).toISOString(),upvoteCount:Number(s.upvoteCount)||0,hasUpvoted:Boolean(s.hasUpvoted)}}validateUpvoteResponse(t){if(!t||"object"!=typeof t)throw new n("Invalid upvote response: expected object");const s=t;if("boolean"!=typeof s.upvoted)throw new n("Invalid upvote response: upvoted must be a boolean");if("number"!=typeof s.upvoteCount)throw new n("Invalid upvote response: upvoteCount must be a number");if("string"!=typeof s.anonymousUserId)throw new n("Invalid upvote response: anonymousUserId must be a string");return{upvoted:s.upvoted,upvoteCount:s.upvoteCount,anonymousUserId:s.anonymousUserId}}getEndpoint(){return this.endpoint}getTimeout(){return this.timeout}}const c=class{constructor(e){t(this,e),this.feedlogUpvote=s(this,"feedlogUpvote"),this.feedlogError=s(this,"feedlogError"),this.maxWidth="42rem",this.theme="light",this.issues=[],this.loading=!0,this.error=null,this.cursor=null,this.hasMore=!1,this.isLoadingMore=!1,this.sdk=null,this.fetchRequestId=0,this.isDisconnected=!1,this.upvoteRequestIds=new Map,this.handleUpvote=async t=>{if(!this.sdk||this.isDisconnected)return;const{issueId:s,currentUpvoted:e,currentCount:i}=t.detail,o=(this.upvoteRequestIds.get(s)||0)+1;this.upvoteRequestIds.set(s,o),this.issues=this.issues.map((t=>t.id===s?Object.assign(Object.assign({},t),{hasUpvoted:!e,upvoteCount:e?i-1:i+1}):t));try{const t=await this.sdk.toggleUpvote(s);if(this.isDisconnected||this.upvoteRequestIds.get(s)!==o)return;this.issues=this.issues.map((e=>e.id===s?Object.assign(Object.assign({},e),{hasUpvoted:t.upvoted,upvoteCount:t.upvoteCount}):e)),this.feedlogUpvote.emit({issueId:s,upvoted:t.upvoted,upvoteCount:t.upvoteCount})}catch(t){if(this.isDisconnected||this.upvoteRequestIds.get(s)!==o)return;this.issues=this.issues.map((t=>t.id===s?Object.assign(Object.assign({},t),{hasUpvoted:e,upvoteCount:i}):t));const r=t instanceof Error?t.message:"Failed to toggle upvote";this.feedlogError.emit({error:r})}}}componentWillLoad(){if(this.previousType=this.type,this.previousLimit=this.limit,this.previousSortBy=this.sortBy,this.initializeSDK(),!(this.issues.length>0)||this.loading)return this.fetchIssues()}disconnectedCallback(){this.isDisconnected=!0,this.fetchRequestId++}componentDidUpdate(){(this.previousType!==this.type||this.previousLimit!==this.limit||this.previousSortBy!==this.sortBy)&&(this.fetchRequestId++,this.cursor=null,this.hasMore=!1,this.issues=[],this.fetchIssues(),this.previousType=this.type,this.previousLimit=this.limit,this.previousSortBy=this.sortBy)}initializeSDK(){try{if(!this.apiKey)throw new Error("API key is required for the Feedlog SDK");this.sdk=new u(Object.assign({apiKey:this.apiKey},this.endpoint&&{endpoint:this.endpoint})),this.error=null}catch(t){const s=t instanceof Error?t.message:"Failed to initialize SDK";this.error=s,this.feedlogError.emit({error:s})}}async fetchIssues(){if(!this.sdk)return;const t=this.fetchRequestId;try{this.loading=!0,this.error=null;const s={};this.type&&(s.type=this.type),this.sortBy&&(s.sortBy=this.sortBy),this.limit&&(s.limit=this.limit),this.cursor&&(s.cursor=this.cursor);const e=await this.sdk.fetchIssues(s);if(this.isDisconnected||t!==this.fetchRequestId)return;this.issues=e.issues,this.cursor=e.pagination.cursor,this.hasMore=e.pagination.hasMore}catch(s){if(this.isDisconnected||t!==this.fetchRequestId)return;const e=s instanceof Error?s.message:"Couldn't load updates";this.error=e,this.issues=[],this.feedlogError.emit({error:e,code:null==s?void 0:s.statusCode})}finally{this.isDisconnected||t!==this.fetchRequestId||(this.loading=!1,this.isLoadingMore=!1)}}async loadMore(){if(!this.sdk||!this.hasMore||this.isLoadingMore||this.loading)return;const t=this.fetchRequestId;this.isLoadingMore=!0;try{const s={};this.type&&(s.type=this.type),this.sortBy&&(s.sortBy=this.sortBy),this.limit&&(s.limit=this.limit),this.cursor&&(s.cursor=this.cursor);const e=await this.sdk.fetchIssues(s);if(this.isDisconnected||t!==this.fetchRequestId)return;this.issues=[...this.issues,...e.issues],this.cursor=e.pagination.cursor,this.hasMore=e.pagination.hasMore}catch(s){if(this.isDisconnected||t!==this.fetchRequestId)return;const e=s instanceof Error?s.message:"Failed to load more issues";this.feedlogError.emit({error:e,code:null==s?void 0:s.statusCode})}finally{this.isDisconnected||t!==this.fetchRequestId||(this.isLoadingMore=!1)}}render(){var t,s;const i=null===(s=null===(t=this.el)||void 0===t?void 0:t.style)||void 0===s?void 0:s.getPropertyValue("--feedlog-background");return e("feedlog-issues",{key:"0528f8bbb5f1e735c3b50a4325221ee8bfdfadda",style:i?{"--feedlog-background":i}:void 0,issues:this.issues,limit:this.limit,maxWidth:this.maxWidth,theme:this.theme,heading:this.heading,subtitle:this.subtitle,emptyStateTitle:this.emptyStateTitle,emptyStateMessage:this.emptyStateMessage,getIssueUrl:this.getIssueUrl,loading:this.loading,error:this.error,hasMore:this.hasMore,isLoadingMore:this.isLoadingMore,onFeedlogUpvote:this.handleUpvote,onFeedlogLoadMore:async()=>this.loadMore()})}get el(){return i(this)}};export{c as feedlog_issues_client}
@@ -89,7 +89,7 @@ export declare class FeedlogIssuesClient {
89
89
  private isDisconnected;
90
90
  /** Map to track the latest upvote request ID for each issue to handle race conditions */
91
91
  private upvoteRequestIds;
92
- componentWillLoad(): void;
92
+ componentWillLoad(): Promise<void> | undefined;
93
93
  disconnectedCallback(): void;
94
94
  componentDidUpdate(): void;
95
95
  private initializeSDK;
package/hydrate/index.js CHANGED
@@ -7240,7 +7240,12 @@ class FeedlogIssuesClient {
7240
7240
  this.previousLimit = this.limit;
7241
7241
  this.previousSortBy = this.sortBy;
7242
7242
  this.initializeSDK();
7243
- this.fetchIssues();
7243
+ // Return the promise so SSR waits for the fetch before serializing HTML.
7244
+ // During client hydration, skip fetch if we already have server-rendered data.
7245
+ if (this.issues.length > 0 && !this.loading) {
7246
+ return;
7247
+ }
7248
+ return this.fetchIssues();
7244
7249
  }
7245
7250
  disconnectedCallback() {
7246
7251
  // Prevent any pending async operations from updating state
@@ -7386,7 +7391,7 @@ class FeedlogIssuesClient {
7386
7391
  const style = hostBg
7387
7392
  ? { '--feedlog-background': hostBg }
7388
7393
  : undefined;
7389
- return (hAsync("feedlog-issues", { key: '15ae96d7d7b51964026f873f8b97e10530ed02b4', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
7394
+ return (hAsync("feedlog-issues", { key: '0528f8bbb5f1e735c3b50a4325221ee8bfdfadda', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
7390
7395
  }
7391
7396
  get el() { return getElement(this); }
7392
7397
  static get cmpMeta() { return {
package/hydrate/index.mjs CHANGED
@@ -7238,7 +7238,12 @@ class FeedlogIssuesClient {
7238
7238
  this.previousLimit = this.limit;
7239
7239
  this.previousSortBy = this.sortBy;
7240
7240
  this.initializeSDK();
7241
- this.fetchIssues();
7241
+ // Return the promise so SSR waits for the fetch before serializing HTML.
7242
+ // During client hydration, skip fetch if we already have server-rendered data.
7243
+ if (this.issues.length > 0 && !this.loading) {
7244
+ return;
7245
+ }
7246
+ return this.fetchIssues();
7242
7247
  }
7243
7248
  disconnectedCallback() {
7244
7249
  // Prevent any pending async operations from updating state
@@ -7384,7 +7389,7 @@ class FeedlogIssuesClient {
7384
7389
  const style = hostBg
7385
7390
  ? { '--feedlog-background': hostBg }
7386
7391
  : undefined;
7387
- return (hAsync("feedlog-issues", { key: '15ae96d7d7b51964026f873f8b97e10530ed02b4', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
7392
+ return (hAsync("feedlog-issues", { key: '0528f8bbb5f1e735c3b50a4325221ee8bfdfadda', style: style, issues: this.issues, limit: this.limit, maxWidth: this.maxWidth, theme: this.theme, heading: this.heading, subtitle: this.subtitle, emptyStateTitle: this.emptyStateTitle, emptyStateMessage: this.emptyStateMessage, getIssueUrl: this.getIssueUrl, loading: this.loading, error: this.error, hasMore: this.hasMore, isLoadingMore: this.isLoadingMore, onFeedlogUpvote: this.handleUpvote, onFeedlogLoadMore: async () => this.loadMore() }));
7388
7393
  }
7389
7394
  get el() { return getElement(this); }
7390
7395
  static get cmpMeta() { return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feedlog-ai/webcomponents",
3
- "version": "0.0.31",
3
+ "version": "0.0.33",
4
4
  "description": "Stencil web components for Feedlog Toolkit - Data visualization components",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "module": "./dist/index.js",
@@ -66,7 +66,7 @@
66
66
  "clean": "rm -rf dist loader"
67
67
  },
68
68
  "dependencies": {
69
- "@feedlog-ai/core": "^0.0.31",
69
+ "@feedlog-ai/core": "^0.0.33",
70
70
  "dompurify": "^3.3.1",
71
71
  "marked": "^17.0.2"
72
72
  },