jarvis/sdk.go
2026-01-09 00:24:04 +02:00

267 lines
7.5 KiB
Go

package main
import (
"encoding/json"
"net/http"
)
// SDKFunction describes a function in the SDK
type SDKFunction struct {
Name string `json:"name"`
Signature string `json:"signature"`
InsertText string `json:"insertText"`
Documentation string `json:"documentation"`
}
// SDKNamespace describes a namespace with functions
type SDKNamespace struct {
Functions []SDKFunction `json:"functions,omitempty"`
}
// SDKField describes a field in an event type
type SDKField struct {
Name string `json:"name"`
Type string `json:"type"`
Doc string `json:"doc,omitempty"`
}
// SDKEventType describes an event type with nested fields
type SDKEventType struct {
Fields []SDKField `json:"fields"`
}
// SDKHookType maps hook names to their event types
type SDKHookTypes map[string]SDKEventType
// SDKSchema is the complete SDK schema for a language
type SDKSchema struct {
Runner SDKNamespace `json:"Runner"`
Events SDKHookTypes `json:"events"`
}
// Event type definitions per hook (language-agnostic field names, language-specific in schema)
var hookEventFields = map[string][]SDKField{
"post.published": {
{Name: "post", Type: "Post", Doc: "The published post"},
{Name: "author", Type: "Author", Doc: "Post author"},
{Name: "blog", Type: "Blog", Doc: "Blog info"},
},
"post.updated": {
{Name: "post", Type: "Post", Doc: "The updated post"},
{Name: "author", Type: "Author", Doc: "Post author"},
{Name: "changes", Type: "Changes", Doc: "What changed"},
},
"comment.validate": {
{Name: "content", Type: "string", Doc: "Comment content"},
{Name: "authorName", Type: "string", Doc: "Author name"},
{Name: "authorEmail", Type: "string", Doc: "Author email"},
{Name: "postSlug", Type: "string", Doc: "Post slug"},
},
"comment.created": {
{Name: "comment", Type: "Comment", Doc: "The created comment"},
{Name: "post", Type: "Post", Doc: "The post"},
},
"member.subscribed": {
{Name: "member", Type: "Member", Doc: "The subscriber"},
{Name: "tier", Type: "Tier", Doc: "Subscription tier"},
},
"content.render": {
{Name: "html", Type: "string", Doc: "HTML content to transform"},
{Name: "post", Type: "Post", Doc: "Post metadata"},
},
"asset.uploaded": {
{Name: "id", Type: "string", Doc: "Asset ID"},
{Name: "url", Type: "string", Doc: "Asset URL"},
{Name: "contentType", Type: "string", Doc: "MIME type"},
{Name: "size", Type: "int", Doc: "Size in bytes"},
{Name: "width", Type: "int", Doc: "Image width (if image)"},
{Name: "height", Type: "int", Doc: "Image height (if image)"},
},
"analytics.sync": {
{Name: "period", Type: "Period", Doc: "Time period"},
{Name: "pageviews", Type: "int", Doc: "Total pageviews"},
{Name: "visitors", Type: "int", Doc: "Unique visitors"},
{Name: "topPages", Type: "[]PageView", Doc: "Top pages"},
},
}
// Nested type definitions
var nestedTypes = map[string][]SDKField{
"Post": {
{Name: "slug", Type: "string"},
{Name: "title", Type: "string"},
{Name: "url", Type: "string"},
{Name: "excerpt", Type: "string"},
{Name: "publishedAt", Type: "string"},
{Name: "updatedAt", Type: "string"},
{Name: "tags", Type: "[]string"},
{Name: "readingTime", Type: "int"},
},
"Author": {
{Name: "name", Type: "string"},
{Name: "email", Type: "string"},
{Name: "avatar", Type: "string"},
},
"Blog": {
{Name: "name", Type: "string"},
{Name: "url", Type: "string"},
},
"Comment": {
{Name: "id", Type: "string"},
{Name: "content", Type: "string"},
{Name: "authorName", Type: "string"},
{Name: "authorEmail", Type: "string"},
{Name: "postSlug", Type: "string"},
{Name: "parentId", Type: "string"},
{Name: "createdAt", Type: "string"},
},
"Member": {
{Name: "email", Type: "string"},
{Name: "name", Type: "string"},
{Name: "subscribedAt", Type: "string"},
},
"Tier": {
{Name: "name", Type: "string"},
{Name: "price", Type: "int"},
},
"Changes": {
{Name: "title", Type: "TitleChange"},
{Name: "content", Type: "bool"},
{Name: "tags", Type: "TagChanges"},
},
"TitleChange": {
{Name: "old", Type: "string"},
{Name: "new", Type: "string"},
},
"TagChanges": {
{Name: "added", Type: "[]string"},
{Name: "removed", Type: "[]string"},
},
"Period": {
{Name: "start", Type: "string"},
{Name: "end", Type: "string"},
},
"PageView": {
{Name: "path", Type: "string"},
{Name: "views", Type: "int"},
},
}
func buildEventTypes(lang string) SDKHookTypes {
events := make(SDKHookTypes)
for hook, fields := range hookEventFields {
// Convert field names for Go (PascalCase)
if lang == "go" {
converted := make([]SDKField, len(fields))
for i, f := range fields {
converted[i] = SDKField{
Name: toPascalCase(f.Name),
Type: f.Type,
Doc: f.Doc,
}
}
events[hook] = SDKEventType{Fields: converted}
} else {
events[hook] = SDKEventType{Fields: fields}
}
}
return events
}
func toPascalCase(s string) string {
if len(s) == 0 {
return s
}
// Simple: just capitalize first letter
return string(s[0]-32) + s[1:]
}
// SDK schemas for each language
var sdkSchemas = map[string]SDKSchema{
"typescript": {
Runner: SDKNamespace{
Functions: []SDKFunction{
{
Name: "log",
Signature: "log(message: string): void",
InsertText: "log(${1:message})",
Documentation: "Log a message to the plugin console",
},
{
Name: "httpRequest",
Signature: "httpRequest(options: HttpRequestOptions): HttpResponse",
InsertText: "httpRequest({\n url: ${1:url},\n method: \"${2:POST}\",\n headers: { \"Content-Type\": \"application/json\" },\n body: ${3:body}\n})",
Documentation: "Make an HTTP request to an external service",
},
},
},
Events: buildEventTypes("typescript"),
},
"go": {
Runner: SDKNamespace{
Functions: []SDKFunction{
{
Name: "Log",
Signature: "Log(message string)",
InsertText: "Log(${1:message})",
Documentation: "Log a message to the plugin console",
},
{
Name: "HttpRequest",
Signature: "HttpRequest(url string, method string, body []byte) (*Response, error)",
InsertText: "HttpRequest(${1:url}, \"${2:POST}\", []byte(${3:body}))",
Documentation: "Make an HTTP request to an external service",
},
},
},
Events: buildEventTypes("go"),
},
}
// Also expose nested types
type FullSDKResponse struct {
Runner SDKNamespace `json:"Runner"`
Events SDKHookTypes `json:"events"`
NestedTypes map[string][]SDKField `json:"nestedTypes"`
}
func sdkHandler(w http.ResponseWriter, r *http.Request) {
language := r.URL.Query().Get("language")
w.Header().Set("Content-Type", "application/json")
if language != "" {
if schema, ok := sdkSchemas[language]; ok {
// Convert nested types for the language
convertedNested := make(map[string][]SDKField)
for typeName, fields := range nestedTypes {
if language == "go" {
converted := make([]SDKField, len(fields))
for i, f := range fields {
converted[i] = SDKField{
Name: toPascalCase(f.Name),
Type: f.Type,
Doc: f.Doc,
}
}
convertedNested[typeName] = converted
} else {
convertedNested[typeName] = fields
}
}
response := FullSDKResponse{
Runner: schema.Runner,
Events: schema.Events,
NestedTypes: convertedNested,
}
json.NewEncoder(w).Encode(response)
return
}
http.Error(w, "Unknown language", http.StatusBadRequest)
return
}
// Return all schemas (without nested types for brevity)
json.NewEncoder(w).Encode(sdkSchemas)
}