package main // HookInfo describes a plugin hook type HookInfo struct { Name string `json:"name"` Label string `json:"label"` Description string `json:"description"` Pattern string `json:"pattern"` // event, validation, transform TestData map[string]any `json:"test_data"` } // Available hooks var hooks = []HookInfo{ { Name: "post.published", Label: "Post Published", Description: "Triggered when a post is published", Pattern: "event", TestData: map[string]any{ "post": map[string]any{ "slug": "hello-world", "title": "Hello World", "url": "/hello-world", "excerpt": "This is a test post for plugin development.", "publishedAt": "2024-01-15T10:30:00Z", "tags": []string{"test", "development"}, "readingTime": 3, }, "author": map[string]any{ "name": "Test Author", "email": "author@example.com", }, "blog": map[string]any{ "name": "Test Blog", "url": "https://test.writekit.dev", }, }, }, { Name: "post.updated", Label: "Post Updated", Description: "Triggered when a post is updated", Pattern: "event", TestData: map[string]any{ "post": map[string]any{ "slug": "hello-world", "title": "Hello World (Updated)", "url": "/hello-world", "excerpt": "This is a test post that was updated.", "publishedAt": "2024-01-15T10:30:00Z", "updatedAt": "2024-01-16T14:00:00Z", "tags": []string{"test", "development", "updated"}, }, "author": map[string]any{ "name": "Test Author", "email": "author@example.com", }, "changes": map[string]any{ "title": map[string]any{"old": "Hello World", "new": "Hello World (Updated)"}, "content": true, "tags": map[string]any{"added": []string{"updated"}, "removed": []string{}}, }, }, }, { Name: "comment.validate", Label: "Comment Validate", Description: "Validate comments before creation", Pattern: "validation", TestData: map[string]any{ "content": "This is a test comment for moderation.", "authorName": "Test User", "authorEmail": "user@example.com", "postSlug": "hello-world", }, }, { Name: "comment.created", Label: "Comment Created", Description: "Triggered when a comment is created", Pattern: "event", TestData: map[string]any{ "comment": map[string]any{ "id": "test-comment-123", "content": "Great post! Thanks for sharing.", "authorName": "Test User", "authorEmail": "user@example.com", "postSlug": "hello-world", "createdAt": "2024-01-15T12:00:00Z", }, "post": map[string]any{ "slug": "hello-world", "title": "Hello World", "url": "/hello-world", }, }, }, { Name: "member.subscribed", Label: "Member Subscribed", Description: "Triggered when a member subscribes", Pattern: "event", TestData: map[string]any{ "member": map[string]any{ "email": "subscriber@example.com", "name": "New Subscriber", "subscribedAt": "2024-01-15T09:00:00Z", }, "tier": map[string]any{ "name": "Free", "price": 0, }, }, }, { Name: "content.render", Label: "Content Render", Description: "Transform rendered HTML", Pattern: "transform", TestData: map[string]any{ "html": "
This is test content.
const x = 1;",
"post": map[string]any{
"slug": "hello-world",
"title": "Hello World",
"tags": []string{"test"},
},
},
},
{
Name: "asset.uploaded",
Label: "Asset Uploaded",
Description: "Triggered when an asset is uploaded",
Pattern: "event",
TestData: map[string]any{
"id": "asset-123",
"url": "https://cdn.example.com/image.webp",
"contentType": "image/webp",
"size": 102400,
"width": 1920,
"height": 1080,
},
},
{
Name: "analytics.sync",
Label: "Analytics Sync",
Description: "Sync analytics data periodically",
Pattern: "event",
TestData: map[string]any{
"period": map[string]any{
"start": "2024-01-08T00:00:00Z",
"end": "2024-01-15T00:00:00Z",
},
"pageviews": 1250,
"visitors": 890,
"topPages": []map[string]any{
{"path": "/hello-world", "views": 450},
{"path": "/about", "views": 230},
},
},
},
}
// Templates organized by hook and language
var templates = map[string]map[string]string{
"post.published": {
"typescript": `export const onPostPublished = (event: PostPublishedEvent): void => {
Runner.log("Post published: " + event.post.title);
// Example: Send Slack notification
Runner.httpRequest({
url: Runner.secrets.SLACK_WEBHOOK,
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: "New post: " + event.post.title + "\n" + event.post.url,
}),
});
};
`,
"go": `package main
func OnPostPublished(event PostPublishedEvent) error {
Runner.Log("Post published: " + event.Post.Title)
// Example: Send Slack notification
Runner.HttpRequest(Runner.Secrets.SlackWebhook, "POST", []byte("{\"text\":\"New post published\"}"))
return nil
}
func main() {}
`,
},
"post.updated": {
"typescript": `export const onPostUpdated = (event: PostUpdatedEvent): void => {
Runner.log("Post updated: " + event.post.title);
// Example: Sync to external CMS
if (event.changes.content) {
Runner.httpRequest({
url: "https://api.example.com/sync",
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
slug: event.post.slug,
title: event.post.title,
url: event.post.url,
}),
});
}
};
`,
"go": `package main
func OnPostUpdated(event PostUpdatedEvent) error {
Runner.Log("Post updated: " + event.Post.Title)
return nil
}
func main() {}
`,
},
"comment.validate": {
"typescript": `export const validateComment = (input: CommentInput): ValidationResult => {
// Example: Simple spam check
const spamWords = ["buy now", "click here", "free money"];
const content = input.content.toLowerCase();
for (const word of spamWords) {
if (content.includes(word)) {
return { allowed: false, reason: "Comment flagged as spam" };
}
}
// Example: Check minimum length
if (input.content.length < 10) {
return { allowed: false, reason: "Comment too short" };
}
return { allowed: true };
};
`,
"go": `package main
import "strings"
func ValidateComment(input CommentInput) (ValidationResult, error) {
// Example: Simple spam check
spamWords := []string{"buy now", "click here", "free money"}
content := strings.ToLower(input.Content)
for _, word := range spamWords {
if strings.Contains(content, word) {
return ValidationResult{Allowed: false, Reason: "Spam detected"}, nil
}
}
return ValidationResult{Allowed: true}, nil
}
func main() {}
`,
},
"comment.created": {
"typescript": `export const onCommentCreated = (event: CommentCreatedEvent): void => {
Runner.log("New comment on: " + event.post.title);
// Example: Send notification
Runner.httpRequest({
url: Runner.secrets.SLACK_WEBHOOK,
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: "New comment by " + event.comment.authorName + " on \"" + event.post.title + "\"",
}),
});
};
`,
"go": `package main
func OnCommentCreated(event CommentCreatedEvent) error {
Runner.Log("New comment on: " + event.Post.Title)
return nil
}
func main() {}
`,
},
"member.subscribed": {
"typescript": `export const onMemberSubscribed = (event: MemberSubscribedEvent): void => {
Runner.log("New subscriber: " + event.member.email);
// Example: Add to email list
Runner.httpRequest({
url: "https://api.buttondown.email/v1/subscribers",
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Token " + Runner.secrets.BUTTONDOWN_API_KEY,
},
body: JSON.stringify({
email: event.member.email,
notes: "Subscribed from blog",
}),
});
};
`,
"go": `package main
func OnMemberSubscribed(event MemberSubscribedEvent) error {
Runner.Log("New subscriber: " + event.Member.Email)
return nil
}
func main() {}
`,
},
"content.render": {
"typescript": `export const renderContent = (input: ContentRenderInput): ContentRenderOutput => {
let html = input.html;
// Example: Add copy button to code blocks
html = html.replace(
/ {
Runner.log("Asset uploaded: " + event.url);
// Example: Backup to external storage
if (event.contentType.startsWith("image/")) {
Runner.httpRequest({
url: "https://api.cloudinary.com/v1_1/demo/image/upload",
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
file: event.url,
folder: "blog-backups",
}),
});
}
};
`,
"go": `package main
func OnAssetUploaded(event AssetUploadedEvent) error {
Runner.Log("Asset uploaded: " + event.Url)
return nil
}
func main() {}
`,
},
"analytics.sync": {
"typescript": `export const onAnalyticsSync = (event: AnalyticsSyncEvent): void => {
Runner.log("Analytics sync: " + event.pageviews + " pageviews");
// Example: Push to external analytics
Runner.httpRequest({
url: "https://api.example.com/analytics",
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + Runner.secrets.ANALYTICS_KEY,
},
body: JSON.stringify({
period: event.period,
pageviews: event.pageviews,
visitors: event.visitors,
topPages: event.topPages,
}),
});
};
`,
"go": `package main
func OnAnalyticsSync(event AnalyticsSyncEvent) error {
Runner.Log("Analytics sync completed")
return nil
}
func main() {}
`,
},
}
// GetTemplate returns the template for a specific hook and language
func GetTemplate(hook, language string) string {
if hookTemplates, ok := templates[hook]; ok {
if template, ok := hookTemplates[language]; ok {
return template
}
}
// Default fallback
return templates["post.published"]["typescript"]
}