package platform import ( "encoding/json" "math" "mime" "net/http" "strconv" "strings" ) func canvasSchema(raw json.RawMessage) int { var d struct { Version int `json:"schema_version"` } _ = json.Unmarshal(raw, &d) return d.Version } func acceptsCanvasV2(r *http.Request) bool { for _, value := range strings.Split(r.Header.Get("Accept"), ",") { typ, params, err := mime.ParseMediaType(strings.TrimSpace(value)) if err == nil && typ == "application/vnd.foundry.canvas+json" && params["version"] == "2" { if value, exists := params["q"]; exists { q, e := strconv.ParseFloat(value, 64) if e != nil || !(q > 0 && q <= 1) { continue } } return true } } return false } func canvasPoint(v any) bool { p, ok := v.(map[string]any) return ok && canvasFields(p, "x", "y") && canvasNumber(p["x"], -1e6, 1e6) && canvasNumber(p["y"], -1e6, 1e6) } func validCanvasEdgeData(v any) bool { d, ok := v.(map[string]any) if !ok || !canvasFields(d, "appearance", "animation", "path", "execution") { return false } for key, value := range d { m, ok := value.(map[string]any) if !ok { return false } switch key { case "appearance": if !canvasFields(m, "color", "width", "opacity", "line", "head", "tail") { return false } for k, v := range m { switch k { case "color": s, ok := v.(string) if !ok || !canvasColor.MatchString(s) { return false } case "width": if !canvasNumber(v, .5, 20) { return false } case "opacity": if !canvasNumber(v, .05, 1) { return false } case "line": if !canvasChoice(v, "solid", "dashed", "dotted") { return false } case "head", "tail": marker, ok := v.(map[string]any) if !ok || !canvasFields(marker, "type", "size", "filled") || !canvasChoice(marker["type"], "none", "open-arrow", "triangle", "circle", "diamond", "square") { return false } if v, exists := marker["size"]; exists && !canvasNumber(v, 4, 32) { return false } if v, exists := marker["filled"]; exists { if _, ok := v.(bool); !ok { return false } } } } case "animation": if !canvasFields(m, "mode", "effect", "direction", "durationMs", "color") { return false } for k, v := range m { switch k { case "mode": if !canvasChoice(v, "off", "always", "executing") { return false } case "effect": if !canvasChoice(v, "flow", "dash", "pulse") { return false } case "direction": if !canvasChoice(v, "forward", "reverse") { return false } case "durationMs": if !canvasNumber(v, 250, 30000) { return false } case "color": s, ok := v.(string) if !ok || !canvasColor.MatchString(s) { return false } } } case "execution": if !canvasFields(m, "enabled") { return false } if v, exists := m["enabled"]; exists { if _, ok := v.(bool); !ok { return false } } case "path": if !canvasFields(m, "type", "startControl", "endControl", "knots") || m["type"] != "bezier-chain" { return false } for _, k := range []string{"startControl", "endControl"} { if v, exists := m[k]; exists && !canvasPoint(v) { return false } } knots, ok := m["knots"].([]any) if !ok || len(knots) > 64 { return false } seen := map[string]bool{} for _, v := range knots { knot, ok := v.(map[string]any) if !ok || !canvasFields(knot, "id", "position", "in", "out", "mode") { return false } id, ok := knot["id"].(string) if !ok || !canvasID.MatchString(id) || seen[id] { return false } seen[id] = true for _, k := range []string{"position", "in", "out"} { if !canvasPoint(knot[k]) { return false } } if !canvasChoice(knot["mode"], "corner", "smooth", "symmetric") { return false } if knot["mode"] != "corner" { a, b := knot["in"].(map[string]any), knot["out"].(map[string]any) ax, ay, bx, by := a["x"].(float64), a["y"].(float64), b["x"].(float64), b["y"].(float64) la, lb := math.Hypot(ax, ay), math.Hypot(bx, by) if la > 0 && lb > 0 && (math.Abs((ax/la)*(by/lb)-(ay/la)*(bx/lb)) > 1e-6 || (ax/la)*(bx/lb)+(ay/la)*(by/lb) > 0) { return false } if knot["mode"] == "symmetric" && math.Abs(la-lb) > 1e-6*math.Max(la, lb) { return false } } } } } return true }