@jamwidgets/astro 0.1.1 → 0.3.1
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.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/loader.d.ts +2 -19
- package/dist/loader.js +3 -1
- package/package.json +12 -9
- package/src/Analytics.astro +30 -0
- package/src/Comments.astro +7 -11
- package/src/Feedback.astro +4 -0
- package/src/Form.astro +7 -11
- package/src/Poll.astro +4 -0
- package/src/PoweredBy.astro +83 -0
- package/src/Reactions.astro +2 -10
- package/src/Subscribe.astro +4 -0
- package/src/SubscribeForm.astro +3 -11
- package/src/Waitlist.astro +4 -0
- package/src/index.ts +6 -0
- package/src/loader.ts +5 -15
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { DEFAULT_ENDPOINT, API_PATH, VISITOR_STORAGE_KEY, type JamWidgetsConfig, type SeriphConfig, // deprecated alias
|
|
8
8
|
type Comment, type ReactionCounts, type FormSubmitResponse, type SubscribeResponse, type JamwidgetsPost, type SeriphPost, // deprecated alias
|
|
9
|
-
type Announcement, type AnnouncementType, type Poll, type PollOption, type PollSettings, type PollWithResults, type ShowResultsMode, type FeedbackType, buildUrl, getSiteKey, getVisitorId, setVisitorId, type SubmitFormOptions, submitForm, type FetchCommentsOptions, fetchComments, type PostCommentOptions, postComment, type FetchReactionsOptions, type FetchReactionsResponse, fetchReactions, type AddReactionOptions, addReaction, type RemoveReactionOptions, removeReaction, type SubscribeOptions, subscribe, type JoinWaitlistOptions, type JoinWaitlistResponse, joinWaitlist, type ViewCountsOptions, type ViewCounts, type RecordViewResponse, getViewCounts, recordView, type SubmitFeedbackOptions, type SubmitFeedbackResponse, submitFeedback, type FetchPollOptions, fetchPoll, type VotePollOptions, type VotePollResponse, votePoll, type FetchAnnouncementsOptions, fetchAnnouncements, type DismissAnnouncementOptions, dismissAnnouncement, type FetchPostsOptions, fetchPosts, type FetchPostOptions, fetchPost, type ControllerStatus, type ControllerListener, type SubscribeState, type FormState, type ReactionsState, type CommentsState, type WaitlistState, type ViewCountsState, type FeedbackState, type PollState, type AnnouncementsState, SubscribeController, WaitlistController, FormController, ReactionsController, CommentsController, ViewCountsController, FeedbackController, PollController, AnnouncementsController, } from "@jamwidgets/core";
|
|
9
|
+
type Announcement, type AnnouncementType, type Poll, type PollOption, type PollSettings, type PollWithResults, type ShowResultsMode, type FeedbackType, buildUrl, getSiteKey, getVisitorId, setVisitorId, type SubmitFormOptions, submitForm, type FetchCommentsOptions, fetchComments, type PostCommentOptions, postComment, type FetchReactionsOptions, type FetchReactionsResponse, fetchReactions, type AddReactionOptions, addReaction, type RemoveReactionOptions, removeReaction, type SubscribeOptions, subscribe, type JoinWaitlistOptions, type JoinWaitlistResponse, joinWaitlist, type ViewCountsOptions, type ViewCounts, type RecordViewResponse, getViewCounts, recordView, type SubmitFeedbackOptions, type SubmitFeedbackResponse, submitFeedback, type FetchPollOptions, fetchPoll, type VotePollOptions, type VotePollResponse, votePoll, type FetchAnnouncementsOptions, fetchAnnouncements, type DismissAnnouncementOptions, dismissAnnouncement, type FetchPostsOptions, fetchPosts, type FetchPostOptions, fetchPost, type SiteConfig, fetchSiteConfig, POWERED_BY_URL, POWERED_BY_LABEL, type ControllerStatus, type ControllerListener, type SubscribeState, type FormState, type ReactionsState, type CommentsState, type WaitlistState, type ViewCountsState, type FeedbackState, type PollState, type AnnouncementsState, SubscribeController, WaitlistController, FormController, ReactionsController, CommentsController, ViewCountsController, FeedbackController, PollController, AnnouncementsController, } from "@jamwidgets/core";
|
|
10
10
|
export { jamwidgetsPostsLoader, seriphPostsLoader, // deprecated alias
|
|
11
11
|
type JamwidgetsPostsLoaderOptions, type SeriphPostsLoaderOptions, } from "./loader.js";
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ export {
|
|
|
9
9
|
// Constants
|
|
10
10
|
DEFAULT_ENDPOINT, API_PATH, VISITOR_STORAGE_KEY,
|
|
11
11
|
// Helpers
|
|
12
|
-
buildUrl, getSiteKey, getVisitorId, setVisitorId, submitForm, fetchComments, postComment, fetchReactions, addReaction, removeReaction, subscribe, joinWaitlist, getViewCounts, recordView, submitFeedback, fetchPoll, votePoll, fetchAnnouncements, dismissAnnouncement, fetchPosts, fetchPost, SubscribeController, WaitlistController, FormController, ReactionsController, CommentsController, ViewCountsController, FeedbackController, PollController, AnnouncementsController, } from "@jamwidgets/core";
|
|
12
|
+
buildUrl, getSiteKey, getVisitorId, setVisitorId, submitForm, fetchComments, postComment, fetchReactions, addReaction, removeReaction, subscribe, joinWaitlist, getViewCounts, recordView, submitFeedback, fetchPoll, votePoll, fetchAnnouncements, dismissAnnouncement, fetchPosts, fetchPost, fetchSiteConfig, POWERED_BY_URL, POWERED_BY_LABEL, SubscribeController, WaitlistController, FormController, ReactionsController, CommentsController, ViewCountsController, FeedbackController, PollController, AnnouncementsController, } from "@jamwidgets/core";
|
|
13
13
|
// Re-export loader (Astro-specific)
|
|
14
14
|
export { jamwidgetsPostsLoader, seriphPostsLoader, // deprecated alias
|
|
15
15
|
} from "./loader.js";
|
package/dist/loader.d.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { fetchPosts as coreFetchPosts, fetchPost as coreFetchPost, type JamwidgetsPost, type SeriphPost, // deprecated alias
|
|
20
20
|
type FetchPostsOptions, type FetchPostOptions } from "@jamwidgets/core";
|
|
21
|
+
import type { Loader } from "astro/loaders";
|
|
21
22
|
export type { JamwidgetsPost, SeriphPost, FetchPostsOptions, FetchPostOptions };
|
|
22
23
|
export { coreFetchPosts as fetchPosts, coreFetchPost as fetchPost };
|
|
23
24
|
export interface JamwidgetsPostsLoaderOptions {
|
|
@@ -34,29 +35,11 @@ export interface JamwidgetsPostsLoaderOptions {
|
|
|
34
35
|
}
|
|
35
36
|
/** @deprecated Use JamwidgetsPostsLoaderOptions instead */
|
|
36
37
|
export type SeriphPostsLoaderOptions = JamwidgetsPostsLoaderOptions;
|
|
37
|
-
interface LoaderContext {
|
|
38
|
-
store: {
|
|
39
|
-
set: (entry: {
|
|
40
|
-
id: string;
|
|
41
|
-
data: JamwidgetsPost;
|
|
42
|
-
}) => void;
|
|
43
|
-
clear: () => void;
|
|
44
|
-
};
|
|
45
|
-
logger: {
|
|
46
|
-
info: (message: string) => void;
|
|
47
|
-
warn: (message: string) => void;
|
|
48
|
-
error: (message: string) => void;
|
|
49
|
-
};
|
|
50
|
-
generateDigest: (data: unknown) => string;
|
|
51
|
-
}
|
|
52
38
|
/**
|
|
53
39
|
* Creates an Astro content loader that fetches posts from Jamwidgets.
|
|
54
40
|
*
|
|
55
41
|
* Posts are fetched at build time and cached by Astro.
|
|
56
42
|
*/
|
|
57
|
-
export declare function jamwidgetsPostsLoader(options: JamwidgetsPostsLoaderOptions):
|
|
58
|
-
name: string;
|
|
59
|
-
load(context: LoaderContext): Promise<void>;
|
|
60
|
-
};
|
|
43
|
+
export declare function jamwidgetsPostsLoader(options: JamwidgetsPostsLoaderOptions): Loader;
|
|
61
44
|
/** @deprecated Use jamwidgetsPostsLoader instead */
|
|
62
45
|
export declare const seriphPostsLoader: typeof jamwidgetsPostsLoader;
|
package/dist/loader.js
CHANGED
|
@@ -43,6 +43,8 @@ export function jamwidgetsPostsLoader(options) {
|
|
|
43
43
|
"X-Jamwidgets-Key": siteKey,
|
|
44
44
|
// Legacy headers for backward compatibility
|
|
45
45
|
"X-Seriph-Key": siteKey,
|
|
46
|
+
// User-Agent to avoid bot detection (Cloudflare, etc.)
|
|
47
|
+
"User-Agent": "JamwidgetsAstroLoader/1.0",
|
|
46
48
|
},
|
|
47
49
|
});
|
|
48
50
|
if (!response.ok) {
|
|
@@ -53,7 +55,7 @@ export function jamwidgetsPostsLoader(options) {
|
|
|
53
55
|
for (const post of data.posts) {
|
|
54
56
|
store.set({
|
|
55
57
|
id: post.slug,
|
|
56
|
-
data: post,
|
|
58
|
+
data: { ...post },
|
|
57
59
|
});
|
|
58
60
|
}
|
|
59
61
|
logger.info(`Loaded ${data.posts.length} posts from Jamwidgets`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jamwidgets/astro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Astro components and content loader for Jamwidgets (forms, comments, reactions, posts)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,8 +23,10 @@
|
|
|
23
23
|
},
|
|
24
24
|
"./Form": "./src/Form.astro",
|
|
25
25
|
"./Comments": "./src/Comments.astro",
|
|
26
|
+
"./PoweredBy": "./src/PoweredBy.astro",
|
|
26
27
|
"./Reactions": "./src/Reactions.astro",
|
|
27
28
|
"./Views": "./src/Views.astro",
|
|
29
|
+
"./Analytics": "./src/Analytics.astro",
|
|
28
30
|
"./Poll": "./src/Poll.astro",
|
|
29
31
|
"./Subscribe": "./src/Subscribe.astro",
|
|
30
32
|
"./SubscribeForm": "./src/SubscribeForm.astro",
|
|
@@ -37,6 +39,10 @@
|
|
|
37
39
|
"src",
|
|
38
40
|
"dist"
|
|
39
41
|
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc",
|
|
44
|
+
"dev": "tsc --watch"
|
|
45
|
+
},
|
|
40
46
|
"keywords": [
|
|
41
47
|
"astro",
|
|
42
48
|
"jamwidgets",
|
|
@@ -50,16 +56,13 @@
|
|
|
50
56
|
],
|
|
51
57
|
"license": "MIT",
|
|
52
58
|
"dependencies": {
|
|
53
|
-
"@jamwidgets/core": "0.
|
|
59
|
+
"@jamwidgets/core": "^0.3.1"
|
|
54
60
|
},
|
|
55
61
|
"peerDependencies": {
|
|
56
|
-
"astro": "^5.0.0"
|
|
62
|
+
"astro": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
|
57
63
|
},
|
|
58
64
|
"devDependencies": {
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
"scripts": {
|
|
62
|
-
"build": "tsc",
|
|
63
|
-
"dev": "tsc --watch"
|
|
65
|
+
"astro": "^7.1.3",
|
|
66
|
+
"typescript": "^5.9.3"
|
|
64
67
|
}
|
|
65
|
-
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Falls back to the `jamwidgets-site-key` meta tag. */
|
|
4
|
+
siteKey?: string;
|
|
5
|
+
/** Defaults to `https://jamwidgets.com`. */
|
|
6
|
+
endpoint?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { siteKey = "", endpoint = "" } = Astro.props;
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
data-jamwidgets-analytics
|
|
14
|
+
data-site-key={siteKey}
|
|
15
|
+
data-endpoint={endpoint}
|
|
16
|
+
hidden
|
|
17
|
+
>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<script>
|
|
21
|
+
import { initAnalytics } from "@jamwidgets/core";
|
|
22
|
+
|
|
23
|
+
const el = document.querySelector("[data-jamwidgets-analytics]") as
|
|
24
|
+
| HTMLElement
|
|
25
|
+
| null;
|
|
26
|
+
initAnalytics({
|
|
27
|
+
siteKey: el?.dataset.siteKey || undefined,
|
|
28
|
+
endpoint: el?.dataset.endpoint || undefined,
|
|
29
|
+
});
|
|
30
|
+
</script>
|
package/src/Comments.astro
CHANGED
|
@@ -35,6 +35,8 @@ const {
|
|
|
35
35
|
|
|
36
36
|
// Build the API URL
|
|
37
37
|
const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
38
|
+
|
|
39
|
+
import PoweredBy from "./PoweredBy.astro";
|
|
38
40
|
---
|
|
39
41
|
|
|
40
42
|
<div
|
|
@@ -85,9 +87,13 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
85
87
|
<div class="jamwidgets-comment-replies"></div>
|
|
86
88
|
</div>
|
|
87
89
|
</template>
|
|
90
|
+
|
|
91
|
+
<PoweredBy siteKey={siteKey} endpoint={endpoint} />
|
|
88
92
|
</div>
|
|
89
93
|
|
|
90
94
|
<script>
|
|
95
|
+
import { getAttributionHeaders, getVisitorId } from "@jamwidgets/core";
|
|
96
|
+
|
|
91
97
|
interface Comment {
|
|
92
98
|
id: string;
|
|
93
99
|
authorName: string;
|
|
@@ -96,17 +102,6 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
96
102
|
replies: Comment[];
|
|
97
103
|
}
|
|
98
104
|
|
|
99
|
-
// Get or create a visitor ID for anonymous users
|
|
100
|
-
const VISITOR_KEY = "jamwidgets_visitor_id";
|
|
101
|
-
function getVisitorId(): string {
|
|
102
|
-
let id = localStorage.getItem(VISITOR_KEY);
|
|
103
|
-
if (!id) {
|
|
104
|
-
id = crypto.randomUUID();
|
|
105
|
-
localStorage.setItem(VISITOR_KEY, id);
|
|
106
|
-
}
|
|
107
|
-
return id;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
105
|
// Store form load timestamps for spam detection
|
|
111
106
|
const formTimestamps = new WeakMap<Element, number>();
|
|
112
107
|
|
|
@@ -126,6 +121,7 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
126
121
|
|
|
127
122
|
const visitorId = getVisitorId();
|
|
128
123
|
const headers = {
|
|
124
|
+
...getAttributionHeaders(pageId),
|
|
129
125
|
"X-JamWidgets-Key": siteKey,
|
|
130
126
|
"X-JamWidgets-Visitor": visitorId,
|
|
131
127
|
};
|
package/src/Feedback.astro
CHANGED
|
@@ -38,6 +38,8 @@ const {
|
|
|
38
38
|
|
|
39
39
|
// Build the API URL
|
|
40
40
|
const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
41
|
+
|
|
42
|
+
import PoweredBy from "./PoweredBy.astro";
|
|
41
43
|
---
|
|
42
44
|
|
|
43
45
|
<div
|
|
@@ -81,6 +83,8 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
81
83
|
</button>
|
|
82
84
|
</div>
|
|
83
85
|
</form>
|
|
86
|
+
|
|
87
|
+
<PoweredBy siteKey={siteKey} endpoint={endpoint} />
|
|
84
88
|
</div>
|
|
85
89
|
|
|
86
90
|
<script>
|
package/src/Form.astro
CHANGED
|
@@ -40,6 +40,8 @@ const {
|
|
|
40
40
|
|
|
41
41
|
// Build the API URL
|
|
42
42
|
const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
43
|
+
|
|
44
|
+
import PoweredBy from "./PoweredBy.astro";
|
|
43
45
|
---
|
|
44
46
|
|
|
45
47
|
<form
|
|
@@ -58,25 +60,18 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
58
60
|
|
|
59
61
|
<!-- Success/Error message container (hidden by default) -->
|
|
60
62
|
<div class="jamwidgets-form-message" style="display: none;" aria-live="polite"></div>
|
|
63
|
+
|
|
64
|
+
<PoweredBy siteKey={siteKey} endpoint={endpoint} />
|
|
61
65
|
</form>
|
|
62
66
|
|
|
63
67
|
<script>
|
|
68
|
+
import { getAttributionHeaders, getVisitorId } from "@jamwidgets/core";
|
|
69
|
+
|
|
64
70
|
interface FormSubmitResponse {
|
|
65
71
|
success: boolean;
|
|
66
72
|
message: string;
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
// Get or create a visitor ID for anonymous users
|
|
70
|
-
const VISITOR_KEY = "jamwidgets_visitor_id";
|
|
71
|
-
function getVisitorId(): string {
|
|
72
|
-
let id = localStorage.getItem(VISITOR_KEY);
|
|
73
|
-
if (!id) {
|
|
74
|
-
id = crypto.randomUUID();
|
|
75
|
-
localStorage.setItem(VISITOR_KEY, id);
|
|
76
|
-
}
|
|
77
|
-
return id;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
75
|
// Store form load timestamps for spam detection
|
|
81
76
|
const formTimestamps = new WeakMap<Element, number>();
|
|
82
77
|
|
|
@@ -130,6 +125,7 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
130
125
|
const response = await fetch(`${endpoint}/forms/${formSlug}/submit`, {
|
|
131
126
|
method: "POST",
|
|
132
127
|
headers: {
|
|
128
|
+
...getAttributionHeaders(),
|
|
133
129
|
"Content-Type": "application/json",
|
|
134
130
|
"X-JamWidgets-Key": siteKey,
|
|
135
131
|
"X-JamWidgets-Visitor": getVisitorId(),
|
package/src/Poll.astro
CHANGED
|
@@ -38,6 +38,8 @@ const {
|
|
|
38
38
|
|
|
39
39
|
// Build the API URL
|
|
40
40
|
const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
41
|
+
|
|
42
|
+
import PoweredBy from "./PoweredBy.astro";
|
|
41
43
|
---
|
|
42
44
|
|
|
43
45
|
<div
|
|
@@ -60,6 +62,8 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
60
62
|
<button type="button" class="jamwidgets-poll-submit" style="display: none;">Vote</button>
|
|
61
63
|
</div>
|
|
62
64
|
<div class="jamwidgets-poll-error" style="display: none;"></div>
|
|
65
|
+
|
|
66
|
+
<PoweredBy siteKey={siteKey} endpoint={endpoint} />
|
|
63
67
|
</div>
|
|
64
68
|
|
|
65
69
|
<script>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* JamWidgets "Powered by" footer.
|
|
4
|
+
*
|
|
5
|
+
* Rendered at the bottom of a drop-in widget. It hydrates hidden and only
|
|
6
|
+
* reveals itself when the site is on the Free tier (no custom branding) — the
|
|
7
|
+
* gate is the `hasCustomBranding` flag from `GET /api/v1/site`. Paid tiers
|
|
8
|
+
* (Pro/Team) keep it hidden, and if the config fetch fails we stay hidden so a
|
|
9
|
+
* paying site is never wrongly branded.
|
|
10
|
+
*
|
|
11
|
+
* Included by the block widgets (Comments, Form, Subscribe, Poll, Feedback,
|
|
12
|
+
* Waitlist); not the ambient ones (Reactions, Views, Announcements).
|
|
13
|
+
*/
|
|
14
|
+
interface Props {
|
|
15
|
+
/** Your site key (required) */
|
|
16
|
+
siteKey: string;
|
|
17
|
+
/** Base URL of your JamWidgets instance (default: 'https://jamwidgets.com') */
|
|
18
|
+
endpoint?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const DEFAULT_ENDPOINT = "https://jamwidgets.com";
|
|
22
|
+
const API_PATH = "/api/v1";
|
|
23
|
+
|
|
24
|
+
const { siteKey, endpoint = DEFAULT_ENDPOINT } = Astro.props;
|
|
25
|
+
const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<div
|
|
29
|
+
class="jamwidgets-powered-by"
|
|
30
|
+
data-jamwidgets-branding
|
|
31
|
+
data-endpoint={baseUrl}
|
|
32
|
+
data-site-key={siteKey}
|
|
33
|
+
hidden
|
|
34
|
+
>
|
|
35
|
+
<a href="https://jamwidgets.com/?utm_source=powered_by" target="_blank" rel="noopener noreferrer">
|
|
36
|
+
Powered by JamWidgets
|
|
37
|
+
</a>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<script>
|
|
41
|
+
// Reveal the footer only for sites without custom branding (Free tier).
|
|
42
|
+
// Site config is fetched once per (endpoint, siteKey); on any failure we
|
|
43
|
+
// leave the footer hidden (fail closed — never brand a paying site).
|
|
44
|
+
const brandingCache = new Map<string, Promise<boolean>>();
|
|
45
|
+
|
|
46
|
+
function hasCustomBranding(baseUrl: string, siteKey: string): Promise<boolean> {
|
|
47
|
+
const key = `${baseUrl}::${siteKey}`;
|
|
48
|
+
let pending = brandingCache.get(key);
|
|
49
|
+
if (!pending) {
|
|
50
|
+
pending = fetch(`${baseUrl}/site`, { headers: { "X-JamWidgets-Key": siteKey } })
|
|
51
|
+
.then((res) => (res.ok ? res.json() : null))
|
|
52
|
+
.then((data) => data?.site_config?.hasCustomBranding === true)
|
|
53
|
+
.catch(() => true);
|
|
54
|
+
brandingCache.set(key, pending);
|
|
55
|
+
}
|
|
56
|
+
return pending;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
document.querySelectorAll<HTMLElement>("[data-jamwidgets-branding]").forEach((el) => {
|
|
60
|
+
const baseUrl = el.dataset.endpoint;
|
|
61
|
+
const siteKey = el.dataset.siteKey;
|
|
62
|
+
if (!baseUrl || !siteKey) return;
|
|
63
|
+
hasCustomBranding(baseUrl, siteKey).then((custom) => {
|
|
64
|
+
if (!custom) el.removeAttribute("hidden");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<style>
|
|
70
|
+
.jamwidgets-powered-by {
|
|
71
|
+
margin-top: 0.75rem;
|
|
72
|
+
text-align: center;
|
|
73
|
+
font-size: 0.75rem;
|
|
74
|
+
line-height: 1;
|
|
75
|
+
}
|
|
76
|
+
.jamwidgets-powered-by a {
|
|
77
|
+
color: var(--jamwidgets-muted-color, #9ca3af);
|
|
78
|
+
text-decoration: none;
|
|
79
|
+
}
|
|
80
|
+
.jamwidgets-powered-by a:hover {
|
|
81
|
+
text-decoration: underline;
|
|
82
|
+
}
|
|
83
|
+
</style>
|
package/src/Reactions.astro
CHANGED
|
@@ -87,16 +87,7 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
87
87
|
</div>
|
|
88
88
|
|
|
89
89
|
<script>
|
|
90
|
-
|
|
91
|
-
const VISITOR_KEY = "jamwidgets_visitor_id";
|
|
92
|
-
function getVisitorId(): string {
|
|
93
|
-
let id = localStorage.getItem(VISITOR_KEY);
|
|
94
|
-
if (!id) {
|
|
95
|
-
id = crypto.randomUUID();
|
|
96
|
-
localStorage.setItem(VISITOR_KEY, id);
|
|
97
|
-
}
|
|
98
|
-
return id;
|
|
99
|
-
}
|
|
90
|
+
import { getAttributionHeaders, getVisitorId } from "@jamwidgets/core";
|
|
100
91
|
|
|
101
92
|
document.querySelectorAll("[data-jamwidgets-reactions]").forEach((container) => {
|
|
102
93
|
const endpoint = (container as HTMLElement).dataset.endpoint;
|
|
@@ -107,6 +98,7 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
107
98
|
|
|
108
99
|
const visitorId = getVisitorId();
|
|
109
100
|
const headers = {
|
|
101
|
+
...getAttributionHeaders(pageId),
|
|
110
102
|
"X-JamWidgets-Key": siteKey,
|
|
111
103
|
"X-JamWidgets-Visitor": visitorId,
|
|
112
104
|
};
|
package/src/Subscribe.astro
CHANGED
|
@@ -49,6 +49,8 @@ const {
|
|
|
49
49
|
|
|
50
50
|
// Build the API URL
|
|
51
51
|
const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
52
|
+
|
|
53
|
+
import PoweredBy from "./PoweredBy.astro";
|
|
52
54
|
---
|
|
53
55
|
|
|
54
56
|
<form
|
|
@@ -79,6 +81,8 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
79
81
|
|
|
80
82
|
<!-- Success/Error message container (hidden by default) -->
|
|
81
83
|
<div class="jamwidgets-subscribe-message" style="display: none;" aria-live="polite"></div>
|
|
84
|
+
|
|
85
|
+
<PoweredBy siteKey={siteKey} endpoint={endpoint} />
|
|
82
86
|
</form>
|
|
83
87
|
|
|
84
88
|
<script>
|
package/src/SubscribeForm.astro
CHANGED
|
@@ -71,22 +71,13 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
71
71
|
</form>
|
|
72
72
|
|
|
73
73
|
<script>
|
|
74
|
+
import { getAttributionHeaders, getVisitorId } from "@jamwidgets/core";
|
|
75
|
+
|
|
74
76
|
interface SubscribeResponse {
|
|
75
77
|
success: boolean;
|
|
76
78
|
message: string;
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
// Get or create a visitor ID for anonymous users
|
|
80
|
-
const VISITOR_KEY = "jamwidgets_visitor_id";
|
|
81
|
-
function getVisitorId(): string {
|
|
82
|
-
let id = localStorage.getItem(VISITOR_KEY);
|
|
83
|
-
if (!id) {
|
|
84
|
-
id = crypto.randomUUID();
|
|
85
|
-
localStorage.setItem(VISITOR_KEY, id);
|
|
86
|
-
}
|
|
87
|
-
return id;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
81
|
document.querySelectorAll("[data-jamwidgets-subscribe-form]").forEach((form) => {
|
|
91
82
|
form.addEventListener("submit", async (e) => {
|
|
92
83
|
e.preventDefault();
|
|
@@ -114,6 +105,7 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
114
105
|
const response = await fetch(`${endpoint}/subscribe`, {
|
|
115
106
|
method: "POST",
|
|
116
107
|
headers: {
|
|
108
|
+
...getAttributionHeaders(),
|
|
117
109
|
"Content-Type": "application/json",
|
|
118
110
|
"X-JamWidgets-Key": siteKey,
|
|
119
111
|
"X-JamWidgets-Visitor": getVisitorId(),
|
package/src/Waitlist.astro
CHANGED
|
@@ -40,6 +40,8 @@ const {
|
|
|
40
40
|
|
|
41
41
|
// Build the API URL
|
|
42
42
|
const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
43
|
+
|
|
44
|
+
import PoweredBy from "./PoweredBy.astro";
|
|
43
45
|
---
|
|
44
46
|
|
|
45
47
|
<div
|
|
@@ -62,6 +64,8 @@ const baseUrl = endpoint.replace(/\/+$/, "") + API_PATH;
|
|
|
62
64
|
</button>
|
|
63
65
|
</form>
|
|
64
66
|
<div class="jamwidgets-waitlist-message" style="display: none;"></div>
|
|
67
|
+
|
|
68
|
+
<PoweredBy siteKey={siteKey} endpoint={endpoint} />
|
|
65
69
|
</div>
|
|
66
70
|
|
|
67
71
|
<script>
|
package/src/index.ts
CHANGED
package/src/loader.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
type FetchPostsOptions,
|
|
29
29
|
type FetchPostOptions,
|
|
30
30
|
} from "@jamwidgets/core";
|
|
31
|
+
import type { Loader, LoaderContext } from "astro/loaders";
|
|
31
32
|
|
|
32
33
|
// Re-export types and functions from core
|
|
33
34
|
export type { JamwidgetsPost, SeriphPost, FetchPostsOptions, FetchPostOptions };
|
|
@@ -49,19 +50,6 @@ export interface JamwidgetsPostsLoaderOptions {
|
|
|
49
50
|
/** @deprecated Use JamwidgetsPostsLoaderOptions instead */
|
|
50
51
|
export type SeriphPostsLoaderOptions = JamwidgetsPostsLoaderOptions;
|
|
51
52
|
|
|
52
|
-
interface LoaderContext {
|
|
53
|
-
store: {
|
|
54
|
-
set: (entry: { id: string; data: JamwidgetsPost }) => void;
|
|
55
|
-
clear: () => void;
|
|
56
|
-
};
|
|
57
|
-
logger: {
|
|
58
|
-
info: (message: string) => void;
|
|
59
|
-
warn: (message: string) => void;
|
|
60
|
-
error: (message: string) => void;
|
|
61
|
-
};
|
|
62
|
-
generateDigest: (data: unknown) => string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
53
|
interface ApiResponse {
|
|
66
54
|
posts: JamwidgetsPost[];
|
|
67
55
|
total: number;
|
|
@@ -72,7 +60,7 @@ interface ApiResponse {
|
|
|
72
60
|
*
|
|
73
61
|
* Posts are fetched at build time and cached by Astro.
|
|
74
62
|
*/
|
|
75
|
-
export function jamwidgetsPostsLoader(options: JamwidgetsPostsLoaderOptions) {
|
|
63
|
+
export function jamwidgetsPostsLoader(options: JamwidgetsPostsLoaderOptions): Loader {
|
|
76
64
|
const {
|
|
77
65
|
endpoint = DEFAULT_ENDPOINT,
|
|
78
66
|
tag,
|
|
@@ -103,6 +91,8 @@ export function jamwidgetsPostsLoader(options: JamwidgetsPostsLoaderOptions) {
|
|
|
103
91
|
"X-Jamwidgets-Key": siteKey,
|
|
104
92
|
// Legacy headers for backward compatibility
|
|
105
93
|
"X-Seriph-Key": siteKey,
|
|
94
|
+
// User-Agent to avoid bot detection (Cloudflare, etc.)
|
|
95
|
+
"User-Agent": "JamwidgetsAstroLoader/1.0",
|
|
106
96
|
},
|
|
107
97
|
});
|
|
108
98
|
|
|
@@ -119,7 +109,7 @@ export function jamwidgetsPostsLoader(options: JamwidgetsPostsLoaderOptions) {
|
|
|
119
109
|
for (const post of data.posts) {
|
|
120
110
|
store.set({
|
|
121
111
|
id: post.slug,
|
|
122
|
-
data: post,
|
|
112
|
+
data: { ...post },
|
|
123
113
|
});
|
|
124
114
|
}
|
|
125
115
|
|